Cierres
Las clausuras son invocables que capturan variables del entorno envolvente. Se pueden crear clausuras de funciones y operaciones. Se puede crear una clausura de una operación dentro de una función, pero solo se puede aplicar en una operación.
Q# tiene dos mecanismos para crear clausuras: expresiones lambda y aplicación parcial.
Expresiones lambda
Una expresión lambda crea una función o una operación anónimas.
La sintaxis básica es una tupla de símbolos para enlazar los parámetros, una flecha (->
para una función y =>
para una operación) y una expresión que se va a evaluar cuando se aplique.
// 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
Parámetros
Los parámetros se enlazan mediante una tupla de símbolos idéntica al lado izquierdo de una instrucción de declaración de variable. El tipo de la tupla de parámetros es implícito. No se admiten anotaciones de tipo; si se produce un error en la inferencia de tipos, es posible que tenga que crear una declaración invocable de nivel superior y usar la aplicación parcial en su lugar.
Variables de captura mutables
No se pueden capturar variables mutables. Si solo tiene que capturar el valor de una variable mutable en el momento en que se crea la expresión lambda, puede crear una copia inmutable:
// ERROR: 'variable' cannot be captured.
mutable variable = 1;
let f = () -> variable;
// OK.
let value = variable;
let g = () -> value;
Características
Las características de una operación anónima se deducen en función de las aplicaciones de la expresión lambda. Si la expresión lambda se usa con una aplicación de functor o en un contexto que espera una característica, la expresión lambda se deduce que tiene esa característica. Por ejemplo:
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 necesita características diferentes de las que se han inferido para una expresión lambda de operación, deberá crear en su lugar una declaración de operación de nivel superior.
Aplicación parcial
La aplicación parcial es una abreviatura conveniente para aplicar algunos argumentos de un invocable, pero no todos.
La sintaxis es la misma que la de una expresión de llamada, pero los argumentos sin aplicar se reemplazan por _
.
Conceptualmente, la aplicación parcial es equivalente a una expresión lambda que captura los argumentos aplicados y toma los argumentos no aplicados como parámetros.
Por ejemplo, dado que f
es una función, o
es una operación y la variable capturada x
es inmutable:
Aplicación parcial | Expresión 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 captura mutables
A diferencia de las expresiones lambda, la aplicación parcial puede capturar automáticamente una copia del valor de una variable mutable:
mutable variable = 1;
let f = Foo(variable, _);
Esto equivale a la siguiente expresión lambda:
mutable variable = 1;
let value = variable;
let f = x -> Foo(value, x);
[^1]: La tupla de parámetros se ha escrito estrictamente como (a, (b))
, pero (b)
es equivalente a b
.