Fermetures
Les fermetures sont des callables qui capturent des variables à partir de l’environnement englobant. Des fermetures de fonction et d’opération peuvent être créées. Une fermeture d’opération peut être créée à l’intérieur d’une fonction, mais elle peut être appliquée uniquement dans une opération.
Q# a deux mécanismes pour créer des fermetures : les expressions lambda et l’application partielle.
Expressions lambda
Une expression lambda crée une fonction ou une opération anonyme.
La syntaxe de base est un tuple de symboles pour lier les paramètres, une flèche (->
pour une fonction et =>
pour une opération) et une expression à évaluer quand elle est appliquée.
// 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
Paramètres
Les paramètres sont liés à l’aide d’un tuple de symboles identique au côté gauche d’une instruction de déclaration de variable. Le type du tuple de paramètres est implicite. Les annotations de type ne sont pas prises en charge. Si l’inférence de type échoue, vous devrez peut-être créer une déclaration de callable de niveau supérieur et utiliser l’application partielle à la place.
Variables de capture mutables
Les variables mutables ne peuvent pas être capturées. Si vous devez uniquement capturer la valeur d’une variable mutable à l’instant où l’expression lambda est créée, vous pouvez créer une copie immuable :
// ERROR: 'variable' cannot be captured.
mutable variable = 1;
let f = () -> variable;
// OK.
let value = variable;
let g = () -> value;
Caractéristiques
Les caractéristiques d’une opération anonyme sont déduites en fonction des applications du lambda. Si l’lambda est utilisé avec une application de foncteur ou dans un contexte qui attend une caractéristique, l’lambda est alors déduit qu’il a cette caractéristique. Par exemple :
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
}
Si vous avez besoin de caractéristiques différentes pour une expression lambda d’opération que ce qui a été déduit, vous devez créer une déclaration d’opération de niveau supérieur à la place.
Application partielle
L’application partielle est un raccourci pratique pour appliquer certains arguments d’un callable, mais pas tous.
La syntaxe est identique à celle d’une expression d’appel, mais les arguments non appliqués sont remplacés par _
.
Conceptuellement, l’application partielle équivaut à une expression lambda qui capture les arguments appliqués et accepte les arguments non appliqués en tant que paramètres.
Par exemple, étant donné que f
est une fonction, que o
est une opération et que la variable capturée x
est immuable :
Application partielle | Expression 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)) |
Variables de capture mutables
Contrairement aux expressions lambda, l’application partielle peut capturer automatiquement une copie de la valeur d’une variable mutable :
mutable variable = 1;
let f = Foo(variable, _);
Cela équivaut à l’expression lambda suivante :
mutable variable = 1;
let value = variable;
let f = x -> Foo(value, x);
[^1] : le tuple de paramètres s’écrit strictement (a, (b))
, mais (b)
équivaut à b
.