Freigeben über


Abschlüsse

Abschlüsse sind aufrufbare Komponenten, die Variablen aus der umschließenden Umgebung erfassen. Es können sowohl Funktionsabschlüsse als auch Vorgangsabschlüsse erstellt werden. Ein Vorgangsabschluss kann innerhalb einer Funktion erstellt, aber nur in einem Vorgang angewendet werden.

Q# verfügt über zwei Mechanismen zum Erstellen von Abschlüssen: Lambdaausdrücke und teilweise Anwendung.

Lambdaausdrücke

Ein Lambdaausdruck erstellt eine anonyme Funktion oder einen anonymen Vorgang. Die grundlegende Syntax ist ein Symboltupel zum Binden der Parameter, ein Pfeil (-> für eine Funktion, => für einen Vorgang) und ein Ausdruck, der beim Anwenden ausgewertet werden soll.

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

Parameter

Parameter werden mit einem Symboltupel gebunden, das mit der linken Seite einer Variablendeklarationsanweisung identisch ist. Der Typ des Parametertupels ist implizit. Typanmerkungen werden nicht unterstützt. Ist die Typableitung nicht erfolgreich, müssen Sie möglicherweise eine übergeordnete aufrufbare Deklaration erstellen und stattdessen die teilweise Anwendung verwenden.

Veränderliche Erfassungsvariablen

Veränderliche Variablen können nicht erfasst werden. Wenn Sie lediglich den Wert einer veränderlichen Variablen bei der Erstellung des Lambdaausdrucks erfassen müssen, 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 des Lambdas abgeleitet. Wenn das Lambda mit einer Funktoranwendung oder in einem Kontext verwendet wird, der ein Merkmal erwartet, wird dem Lambda dieses Merkmal abgeleitet. 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 für einen Vorgangslambdaausdruck Merkmale benötigen, die sich von dem Ergebnis der Ableitung unterscheiden, müssen Sie stattdessen eine übergeordnete Deklaration erstellen.

Teilweise Anwendung

Die teilweise Anwendung ist eine praktische Kurzform für die Anwendung einiger, aber nicht aller Argumente einer aufrufbaren Komponente. Die Syntax ist die gleiche wie bei einem Aufrufausdruck, nicht angewendete Argumente werden jedoch durch _ ersetzt. Das Konzept der teilweisen Anwendung entspricht dem eines Lambdaausdrucks, der die angewendeten Argumente erfasst und die nicht verwendeten Argumente als Parameter übernimmt.

Da es sich bei f beispielsweise um eine Funktion und bei o um einen Vorgang handelt, ist die erfasste Variable x unveränderlich:

Teilweise Anwendung 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änderliche Erfassungsvariablen

Im Gegensatz zu Lambda-Ausdrücken kann bei einer teilweisen Anwendung automatisch eine Kopie des Werts einer veränderlichen Variablen erfasst werden:

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

Dies entspricht dem folgenden Lambdaausdruck:

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

[^1]: Der Parametertupel ist streng genommen (a, (b)), aber (b) entspricht b.