Declaraciones invocables

Las declaraciones invocables o las llamadas declaradas en un ámbito global son visibles públicamente de forma predeterminada; es decir, se pueden usar en cualquier parte del mismo proyecto y en un proyecto que haga referencia al ensamblado en el que se declaran. Modificadores de acceso permiten restringir la visibilidad de los mismos solo al ensamblaje actual, de forma que los detalles de implementación puedan cambiarse posteriormente sin romper el código que depende de una biblioteca específica.

Q# admite dos tipos de invocables: operaciones y funciones. El tema Operaciones y Funciones explica la distinción entre ambas. Q# también admite la definición de plantillas; por ejemplo, implementaciones con parámetros de tipo para una invocación determinada Para más información, vea Parametrizaciones de tipo.

Nota:

Estas implementaciones parametrizadas por tipo no pueden utilizar ninguna construcción del lenguaje que dependa de propiedades particulares de los argumentos de tipo; actualmente no hay forma de expresar restricciones de tipo en Q#, o de definir implementaciones especializadas para argumentos de tipo particulares.

Invocables y funtores

Q# permite implementaciones especializadas para propósitos específicos; por ejemplo, las operaciones en Q# pueden definir implícita o explícitamente el soporte para ciertos funtores, y junto con ello las implementaciones especializadas a invocar cuando un funtor específico se aplica a ese invocable.

Un functor, es en cierto sentido, una fábrica que define una nueva implementación de invocables que tiene una relación específica con el invocable al que se aplicó. Los funtores son más que las funciones tradicionales de nivel superior en el sentido de que requieren acceso a los detalles de implementación de la llamada a la que se aplican. En ese sentido, son similares a otras fábricas, como las plantillas. También se pueden aplicar a las llamadas con parámetros de tipo.

Tenga en cuenta la siguiente operación: ApplyQFT

    operation ApplyQFT(qs : Qubit[]) : Unit is Adj + Ctl {
        let length = Length(qs);
        Fact(length >= 1, "ApplyQFT: Length(qs) must be at least 1.");
        for i in length - 1..-1..0 {
            H(qs[i]);
            for j in 0..i - 1 {
                Controlled R1Frac([qs[i]], (1, j + 1, qs[i - j - 1]));
            }
        }
    }

Esta operación toma un argumento de tipo Qubit[] y devuelve un valor de tipo Unit. La anotación is Adj + Ctl en la declaración de ApplyQFT indica que la operación admite tanto el functor Adjoint como el Controlled. (Para más información, veaCaracterísticas de la operación) La expresión Adjoint ApplyQFT tiene acceso a la especialización que implementa el adyacente de ApplyQFTy Controlled ApplyQFT tiene acceso a la especialización que implementa la versión controlada de ApplyQFT. Además del argumento de la operación original, la versión controlada de una operación toma una matriz de cúbits de control y aplica la operación original en la condición de que todos estos cúbits de control estén en un estado |1⟩.

En teoría, una operación para la que se puede definir una versión adjunta debería tener también una versión controlada y viceversa. En la práctica, sin embargo, puede ser difícil desarrollar una implementación para una u otra, especialmente para implementaciones probabilísticas que siguen un patrón de repetición hasta lograr el éxito. Por esa razón, Q# permite declarar el soporte para cada functor individualmente Sin embargo, dado que los dos funtores se intercambian, una operación que defina soporte para ambos también tiene que tener una implementación (normalmente definida implícitamente, es decir, generada por el compilador) para cuando ambos funtores se aplican a la operación.

No hay functors que se puedan aplicar a las funciones. Actualmente, las funciones tienen exactamente una implementación de cuerpo y ninguna especialización adicional. Por ejemplo, la declaración

    function Hello (name : String) : String {
        $"Hello, {name}!"
    }

es equivalente a

    function Hello (name : String) : String {
        body ... {
            $"Hello, {name}!"
        }
    }

Aquí, body especifica que la implementación dada se aplica al cuerpo por defecto de la función Hello, lo que significa que la implementación se invoca cuando no se han aplicado functores u otros mecanismos de fábrica antes de la invocación. Los tres puntos en body ... corresponden a una directiva del compilador que indica que los elementos argumentales de la declaración de la función deben copiarse y pegarse en este lugar.

Los motivos subyacentes indican explícitamente dónde se copiarán los argumentos de la declaración invocable primaria y pegarse son dosfolding: uno, no es necesario repetir la declaración de argumento y dos, garantiza que los functores que requieren argumentos adicionales, como el Controlled functor, se pueden introducir de forma coherente.

Cuando hay exactamente una especialización que define la implementación del cuerpo predeterminado, se puede omitir el ajuste adicional del formulario body ... { <implementation> } .

Recursividad

Los invocables de Q# pueden ser directa o indirectamente recursivos y pueden declararse en cualquier orden; una operación o función puede invocarse a sí misma, o puede invocar a otro invocable que llame directa o indirectamente al invocante.

Cuando se ejecuta en hardware cuántico, el espacio de la pila puede verse limitado, y las recursividades que exceden ese límite de espacio de la pila dan lugar a un error en tiempo de ejecución.