Zamknięcia

Zamknięcia są wywoływane, które przechwytują zmienne z otaczającego środowiska. Można utworzyć zarówno zamknięcia funkcji, jak i operacji. Zamknięcie operacji można utworzyć wewnątrz funkcji, ale można ją zastosować tylko w operacji.

Q# ma dwa mechanizmy tworzenia zamknięć: wyrażenia lambda i częściowa aplikacja.

Wyrażenia lambda

Wyrażenie lambda tworzy funkcję anonimową lub operację. Podstawowa składnia to krotka symboli umożliwiająca powiązanie parametrów, strzałka (-> dla funkcji i => operacji) oraz wyrażenie do oceny w przypadku zastosowania.

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

Parametry

Parametry są powiązane przy użyciu krotki symbolu, która jest identyczna z lewą stroną instrukcji deklaracji zmiennej. Typ krotki parametru jest niejawny. Adnotacje typów nie są obsługiwane; Jeśli wnioskowanie typu nie powiedzie się, może być konieczne utworzenie deklaracji z możliwością wywołania najwyższego poziomu i użycie częściowej aplikacji.

Zmienne przechwytywania modyfikowalnego

Nie można przechwycić zmiennych modyfikowalnych. Jeśli musisz przechwycić tylko wartość zmiennej modyfikowalnej w momencie utworzenia wyrażenia lambda, możesz utworzyć niezmienną kopię:

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

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

Właściwości

Cechy operacji anonimowej są wnioskowane na podstawie zastosowań lambda. Jeśli lambda jest używana z aplikacją functor lub w kontekście, który oczekuje charakterystyki, lambda jest następnie wnioskowany, aby mieć te cechy. Na przykład:

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
}

Jeśli potrzebujesz różnych cech operacji lambda niż to, co zostało wywnioskowane, musisz utworzyć deklarację operacji najwyższego poziomu.

Aplikacja częściowa

Częściowa aplikacja jest wygodnym skrótem do stosowania niektórych, ale nie wszystkich argumentów wywoływanego. Składnia jest taka sama jak wyrażenie wywołania, ale niezastosowane argumenty są zastępowane ciągiem _. Koncepcyjnie częściowa aplikacja jest równoważna wyrażeniu lambda, które przechwytuje zastosowane argumenty i przyjmuje niezastosowane argumenty jako parametry.

Na przykład, biorąc pod uwagę, że f jest funkcją i o jest operacją, a przechwycona zmienna x jest niezmienna:

Aplikacja częściowa Wyrażenie lambda
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))

Zmienne przechwytywania modyfikowalnego

W przeciwieństwie do wyrażeń lambda częściowa aplikacja może automatycznie przechwytywać kopię wartości zmiennej modyfikowalnej:

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

Jest to równoważne następującemu wyrażeniu lambda:

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

[^1]: Krotka parametrów jest ściśle napisana (a, (b)), ale (b) jest równoważna b.