Udostępnij za pośrednictwem


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ęściową aplikację.

Wyrażenia lambda

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

// 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 symboli identycznej z lewej strony instrukcji deklaracji zmiennej . Typ krotki parametrów jest niejawny. Adnotacje typu 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;

Charakterystyka

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 wnioskowane, 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 dla operacji lambda niż to, co zostało wywnioskowane, musisz utworzyć deklarację operacji najwyższego poziomu.

Częściowa aplikacja

Częściowa aplikacja jest wygodnym skrótem do stosowania niektórych, ale nie wszystkich argumentów elementu wywołującego. Składnia jest taka sama jak wyrażenie wywołania, ale niezastosowane argumenty są zastępowane _. 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ą, a o jest operacją, a przechwycona zmienna x jest niezmienna:

Częściowa aplikacja 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.