クロージャ
クロージャは、外側の環境から変数をキャプチャする callable です。 関数と演算の両方のクロージャを作成できます。 演算のクロージャは関数内に作成できますが、その適用先は演算に限られます。
Q# には、クロージャを作成するためのメカニズムとして、ラムダ式と部分適用の 2 つがあります。
ラムダ式
ラムダ式は、匿名の関数または演算を作成するものです。
パラメーターをバインドするためのシンボル タプル、矢印 (関数の場合は ->
、演算の場合は =>
)、そして適用時に評価する式が、基本的な構文となります。
// 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
}
推測されたものとは異なる特性が演算ラムダに必要な場合、最上位の演算宣言を作成する必要があります。
部分適用
部分適用は、callable の引数の一部だけを適用するための便利な省略表現です。
構文は呼び出し式と同じですが、適用されない引数は _
に置き換えられます。
概念上、部分適用は、適用された引数をキャプチャし、適用されていない引数をパラメーターとして受け取るラムダ式に相当します。
たとえば、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
と等価です。