次の方法で共有


クロージャ

クロージャは、外側の環境から変数をキャプチャする 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 と等価です