Freigeben über


Abschlüsse

Schließungen sind Aufrufe, die Variablen aus der eingeschlossenen Umgebung erfassen. Sowohl Funktions- als auch Betriebsschließungen können erstellt werden. Ein Vorgangsabschluss kann innerhalb einer Funktion erstellt werden, kann aber nur in einem Vorgang angewendet werden.

Q# verfügt über zwei Mechanismen zum Erstellen von Schließungen: Lambda-Ausdrücke und teilweise Anwendung.

Lambdaausdrücke

Ein Lambda-Ausdruck erstellt eine anonyme Funktion oder einen anonymen Vorgang. Die grundlegende Syntax ist ein Symbol-Tupel zum Binden der Parameter, eines Pfeils (-> für eine Funktion und => für einen Vorgang) und einen Ausdruck, der ausgewertet werden soll, wenn er angewendet wird.

// 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

Die Parameter

Parameter werden mithilfe eines Symbol-Tupels gebunden, das mit der linken Seite einer Variablendeklarationsanweisungidentisch ist. Der Typ des Parameter-Tupels ist implizit. Typanmerkungen werden nicht unterstützt; Wenn die Typableitung fehlschlägt, müssen Sie möglicherweise eine aufrufbare Deklaration auf oberster Ebene erstellen und stattdessen partielle Anwendung verwenden.

Veränderbare Erfassungsvariablen

Veränderbare Variablen können nicht erfasst werden. Wenn Sie nur den Wert einer veränderbaren Variablen erfassen müssen, sobald der Lambda-Ausdruck erstellt wird, können Sie eine unveränderliche Kopie erstellen:

// ERROR: 'variable' cannot be captured.
mutable variable = 1;
let f = () -> variable;

// OK.
let value = variable;
let g = () -> value;

Merkmale

Die Merkmale eines anonymen Vorgangs werden basierend auf den Anwendungen der Lambda-Funktion abgeleitet. Wenn die Lambda-Funktion mit einer Functor-Anwendung oder in einem Kontext verwendet wird, der ein Merkmal erwartet, wird die Lambda-Funktion abgeleitet, um diese Eigenschaft zu haben. Beispiel:

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
}

Wenn Sie unterschiedliche Merkmale für eine Vorgangs-Lambda-Funktion benötigen als das, was abgeleitet wurde, müssen Sie stattdessen eine Vorgangsdeklaration auf oberster Ebene erstellen.

Teilanwendung

Teilanwendung ist eine praktische Kurzform zum Anwenden einiger, aber nicht aller Argumente eines aufrufbaren Benutzers. Die Syntax ist identisch mit einem Aufrufausdruck, aber nicht angewendete Argumente werden durch _ersetzt. Konzeptionelle Anwendung entspricht einem Lambda-Ausdruck, der die angewendeten Argumente erfasst und die nicht angewendeten Argumente als Parameter einnimmt.

Wenn f beispielsweise eine Funktion ist und o ein Vorgang ist und die erfasste Variable x unveränderlich ist:

Teilanwendung Lambdaausdruck
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))

Veränderbare Erfassungsvariablen

Im Gegensatz zu Lambda-Ausdrücken kann die partielle Anwendung automatisch eine Kopie des Werts einer veränderbaren Variablen erfassen:

mutable variable = 1;
let f = Foo(variable, _);

Dies entspricht dem folgenden Lambda-Ausdruck:

mutable variable = 1;
let value = variable;
let f = x -> Foo(value, x);

[^1]: Das Parameter-Tupel ist streng geschrieben (a, (b)), aber (b) entspricht b.