클로저
클로저는 바깥쪽 환경에서 변수를 캡처하는 호출 가능 항목입니다. 함수와 연산 클로저를 모두 만들 수 있습니다. 연산 클로저는 함수 내에서 만들 수 있지만 연산에서만 적용할 수 있습니다.
Q#에는 클로저를 만드는 람다 식과 부분 애플리케이션이라는 두 가지 메커니즘이 있습니다.
람다 식
람다 식은 익명 함수 또는 연산을 만듭니다.
기본 구문은 매개 변수를 바인딩하는 기호 튜플, 화살표(함수는 ->
, 연산은 =>
) 및 적용 시 평가할 식입니다.
// Function that captures 'x':
y -> x + y
// Operation that captures 'qubit':
deg => Rx(deg * PI() / 180.0, qubit)
// Function that captures nothing:
(x, y) -> x + y
매개 변수
매개 변수는 변수 선언 문의 왼쪽과 동일한 기호 튜플을 사용하여 바인딩됩니다. 매개 변수 튜플의 형식은 암시적입니다. 형식 주석은 지원되지 않습니다. 형식 유추에 실패하면 그 대신 최상위 호출 가능 선언을 만들고 부분 애플리케이션을 사용해야 할 수도 있습니다.
변경 가능한 캡처 변수
변경 가능한 변수는 캡처할 수 없습니다. 람다 식을 만들 때 변경 가능한 변수의 값만 캡처해야 하는 경우 다음과 같이 변경할 수 없는 복사본을 만들면 됩니다.
// ERROR: 'variable' cannot be captured.
mutable variable = 1;
let f = () -> variable;
// OK.
let value = variable;
let g = () -> value;
특징
익명 작업의 특성은 람다의 애플리케이션에 따라 유추됩니다. 람다가 펀터 애플리케이션 또는 특성을 예상하는 컨텍스트에서 사용되는 경우 람다는 해당 특성을 갖도록 유추됩니다. 예를 들면 다음과 같습니다.
operation NoOp(q : Qubit) : Unit is Adj {}
operation Main() : Unit {
use q = Qubit();
let foo = () => NoOp(q);
foo(); // Has type Unit => Unit with no characteristics
let bar = () => NoOp(q);
Adjoint bar(); // Has type Unit => Unit is Adj
}
유추된 특징과 다른 특징이 연산 람다에 필요한 경우 최상위 연산 선언을 대신 만들어야 합니다.
부분 애플리케이션
부분 애플리케이션은 호출 가능 인수의 일부(전부는 아님)를 적용하는 편리한 약식입니다.
구문은 호출 식과 동일하지만, 적용되지 않은 인수는 _
로 대체됩니다.
개념적으로 부분 애플리케이션은 적용된 인수를 캡처하고 적용되지 않은 인수를 매개 변수로 사용하는 람다 식과 동일합니다.
예를 들어 f
가 함수이고 o
가 연산이면 캡처된 변수 x
는 변경할 수 없습니다.
부분 애플리케이션 | 람다 식 |
---|---|
f(x, _) |
a -> f(x, a) |
o(x, _) |
a => o(x, a) |
f(_, (1, _)) |
(a, b) -> f(a, (1, b)) [^1] |
f((_, _, x), (1, _)) |
((a, b), c) -> f((a, b, x), (1, c)) |
변경 가능한 캡처 변수
람다 식과 달리 부분 애플리케이션은 변경 가능한 변수 값의 복사본을 자동으로 캡처할 수 있습니다.
mutable variable = 1;
let f = Foo(variable, _);
이는 다음 람다 식과 동일합니다.
mutable variable = 1;
let value = variable;
let f = x -> Foo(value, x);
[^1]: 매개 변수 튜플은 엄격하게 작성되는 (a, (b))
이지만, (b)
는 b
와 동일합니다.