Declarações que podem ser chamadas

Por predefinição, as declarações inchamáveis ou caláveis declaradas num âmbito global são publicamente visíveis; ou seja, podem ser utilizados em qualquer parte do mesmo projeto e num projeto que faça referência à assemblagem na qual são declarados. Os modificadores de acesso permitem-lhe restringir a respetiva visibilidade apenas à assemblagem atual, de modo a que os detalhes de implementação possam ser alterados mais tarde sem quebrar o código que depende de uma biblioteca específica.

Q# suporta dois tipos de chamadas: operações e funções. O tópico Operações e Funções explica a distinção entre os dois. Q# também suporta a definição de modelos; por exemplo, implementações com parâmetros de tipo para um determinado callable. Para obter mais informações, veja Type parameterizations (Parâmetros de tipo).

Nota

Estas implementações parametrizadas por tipos podem não utilizar construções de idiomas que dependam de propriedades específicas dos argumentos do tipo; Atualmente, não existe forma de expressar restrições de tipo no Q#ou de definir implementações especializadas para argumentos de tipo específicos.

Calíveis e functores

Q# permite implementações especializadas para fins específicos; por exemplo, as operações no Q# podem definir implicitamente ou explicitamente o suporte para determinados functores e, juntamente com ele, as implementações especializadas a invocar quando um functor específico é aplicado a esse funcionável.

Um functor, de certa forma, é uma fábrica que define uma nova implementação callable que tem uma relação específica com o callable a que foi aplicado. Os funtores são mais do que as funções tradicionais de nível superior na qual necessitam de acesso aos detalhes de implementação do callable a que foram aplicados. Nesse sentido, são semelhantes a outras fábricas, como modelos. Também podem ser aplicadas a chamadas com parâmetros de tipo.

Considere a seguinte operação: 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 operação utiliza um argumento do tipo Qubit[] e devolve um valor do tipo Unit. A anotação is Adj + Ctl na declaração de ApplyQFT indica que a operação suporta tanto o Adjoint functor como o Controlled functor. (Para obter mais informações, veja Características da operação). A expressão Adjoint ApplyQFT acede à especialização que implementa o adjacente de ApplyQFTe Controlled ApplyQFT acede à especialização que implementa a versão controlada do ApplyQFT. Além do argumento da operação original, a versão controlada de uma operação utiliza uma matriz de qubits de controlo e aplica a operação original na condição de todos estes qubits de controlo estarem num estado |1⟩.

Em teoria, uma operação para a qual uma versão adjacente pode ser definida também deve ter uma versão controlada e vice-versa. Na prática, no entanto, pode ser difícil desenvolver uma implementação para uma ou outra, especialmente para implementações probabilísticas seguindo um padrão de repetição até ao sucesso. Por esse motivo, Q# permite-lhe declarar suporte para cada funtor individualmente. No entanto, uma vez que os dois functores se deslocam, uma operação que define o suporte para ambos também tem de ter uma implementação (geralmente definida implicitamente, ou seja, gerada pelo compilador) para quando ambos os functores são aplicados à operação.

Não existem functores que possam ser aplicados a funções. Atualmente, as funções têm exatamente uma implementação corporal e não existem mais especializações. Por exemplo, a declaração

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

é equivalente a

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

Aqui, body especifica que a determinada implementação se aplica ao corpo predefinido da função Hello, o que significa que a implementação é invocada quando não foram aplicados functores ou outros mecanismos de fábrica antes da invocação. Os três pontos em body ... correspondem a uma diretiva de compilador que indica que os itens de argumento na declaração de função devem ser copiados e colados neste local.

As razões por trás de indicar explicitamente onde os argumentos da declaração callable principal devem ser copiados e colados são duplos: um, é desnecessário repetir a declaração de argumento, e dois, garante que os functores que necessitam de argumentos adicionais, como o Controlled functor, podem ser introduzidos de forma consistente.

Quando existe exatamente uma especialização que define a implementação do corpo predefinido, o encapsulamento adicional do formulário body ... { <implementation> } pode ser omitido.

Recursão

Q# os calíveis podem ser recursivos direta ou indiretamente e podem ser declarados por qualquer ordem; uma operação ou função pode chamar-se a si mesma ou pode chamar outro callable que chama direta ou indiretamente o autor da chamada.

Ao executar o hardware quântico, o espaço na pilha pode ser limitado e as recursões que excedem esse limite de espaço de pilha resultam num erro de runtime.