Compartir a través de


Declaraciones invocables

Las declaraciones invocables o invocables, 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 Access permiten restringir su visibilidad solo al ensamblado actual, de modo que los detalles de implementación se pueden cambiar más adelante sin interrumpir el código que se basa en una biblioteca específica.

Q# admite dos tipos de invocables: operaciones y funciones. El tema Operations and Functions elabora sobre la distinción entre los dos. Q# también admite la definición de plantillas de ; por ejemplo, implementaciones con parámetros de tipo para un determinado invocable. Para obtener más información, vea Parámetros de tipo.

Nota:

Estas implementaciones parametrizadas de tipo no pueden usar ninguna construcción de lenguaje que dependa de propiedades concretas de los argumentos de tipo; actualmente no hay ninguna manera de expresar restricciones de tipo en Q#, o para definir implementaciones especializadas para argumentos de tipo concretos.

Invocables y functores

Q# permite implementaciones especializadas con fines específicos; Por ejemplo, las operaciones de Q# pueden definir implícita o explícitamente la compatibilidad con determinados functores de y, junto con ella, las implementaciones especializadas que se invocarán cuando se aplica un functor específico a esa función invocable.

Un functor, en cierto sentido, es una fábrica que define una nueva implementación invocable que tiene una relación específica con el invocable al que se aplicó. Los functores son más que las funciones de nivel superior tradicionales en las que requieren acceso a los detalles de implementación del invocable al que se han aplicado. En ese sentido, son similares a otras fábricas, como plantillas. También se pueden aplicar a los invocables 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 Adjoint como el functor de Controlled. (Para obtener más información, vea Características de operación). La expresión Adjoint ApplyQFT accede a la especialización que implementa el adyacente de ApplyQFTy Controlled ApplyQFT accede 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 adyacente también debe tener una versión controlada y viceversa. Sin embargo, en la práctica puede ser difícil desarrollar una implementación para una o la otra, especialmente para implementaciones probabilísticas siguiendo un patrón de repetición hasta el éxito. Por ese motivo, Q# le permite declarar compatibilidad para cada functor individualmente. Sin embargo, dado que los dos functors conmutan, una operación que define la compatibilidad para ambos también tiene que tener una implementación (normalmente definida implícitamente, lo que significa que se genera el compilador) para cuando se aplican ambos functores 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 predeterminado de la función Hello, lo que significa que la implementación se invoca cuando no se han aplicado functors u otros mecanismos de fábrica antes de la invocación. Los tres puntos de body ... corresponden a una directiva del compilador que indica que los elementos de argumento de la declaración de función deben copiarse y pegarse en este punto.

Los motivos subyacentes indican explícitamente dónde se copian los argumentos de la declaración al que se puede llamar primario y se pegan son dosfolding: uno, no es necesario repetir la declaración de argumento y dos, garantiza que los functores que requieren argumentos adicionales, como el functor de Controlled, 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

Q# los invocables pueden ser directamente o indirectamente recursivos y pueden declararse en cualquier orden; una operación o función puede llamarse a sí misma o puede llamar a otra que llame directa o indirectamente al autor de la llamada.

El espacio de pila puede estar limitado al ejecutarse en hardware cuántico y las recursiones que superan ese límite de espacio de pila producen un error en tiempo de ejecución.