Compartilhar via


Declarações que podem ser chamadas

Declarações callable ou callables, declaradas em um escopo global, são publicamente visíveis por padrão; ou seja, eles podem ser usados em qualquer lugar no mesmo projeto e em um projeto que faz referência ao assembly no qual eles são declarados. modificadores do Access permitem que você restrinja sua visibilidade apenas ao assembly atual, de modo que os detalhes da implementação possam ser alterados posteriormente sem quebrar o código que depende de uma biblioteca específica.

Q# dá suporte a dois tipos de callables: operações e funções. O tópico Operações e Funções elaborado sobre a distinção entre os dois. Q# também dá suporte à definição de modelos ; por exemplo, implementações com parâmetros de tipo para um determinado callable. Para obter mais informações, consulte Parametrizações de tipo.

Observação

Essas implementações parametrizadas por tipo podem não usar nenhum constructo de linguagem que dependa de propriedades específicas dos argumentos de tipo; Atualmente, não há como expressar restrições de tipo em Q#ou definir implementações especializadas para argumentos de tipo específicos.

Textos explicativos e functors

Q# permite implementações especializadas para fins específicos; por exemplo, as operações em Q# podem definir implicitamente ou explicitamente o suporte para determinados functorse, juntamente com ela, as implementações especializadas a serem invocadas quando um functor específico é aplicado a esse funl.

Um functor, de certa forma, é uma fábrica que define uma nova implementação callable que tem uma relação específica com o chamado ao qual foi aplicado. Os functors são mais do que as funções tradicionais de nível superior, pois exigem acesso aos detalhes de implementação do chamado ao qual foram aplicados. Nesse sentido, eles são semelhantes a outras fábricas, como modelos. Eles também podem ser aplicados a callables 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]));
            }
        }
    }

Essa operação usa um argumento do tipo Qubit[] e retorna um valor do tipo Unit. A anotação is Adj + Ctl na declaração de ApplyQFT indica que a operação dá suporte ao Adjoint e ao functor Controlled. (Para obter mais informações, consulte Características da operação). A expressão Adjoint ApplyQFT acessa a especialização que implementa o adjacente de ApplyQFTe Controlled ApplyQFT acessa a 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 usa uma matriz de qubits de controle e aplica a operação original com a condição de que todos esses qubits de controle estejam em um 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é o sucesso. Por esse motivo, Q# permite que você declare suporte para cada functor individualmente. No entanto, como os dois functors se deslocam, uma operação que define o suporte para ambos também precisa ter uma implementação (geralmente definida implicitamente, ou seja, gerada pelo compilador) para quando ambos os functors são aplicados à operação.

Não há functors que possam ser aplicados a funções. Atualmente, as funções têm exatamente uma implementação de corpo e nenhuma especialização adicional. 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 implementação fornecida se aplica ao corpo padrão da função Hello, o que significa que a implementação é invocada quando nenhum functor ou outros mecanismos de fábrica foram aplicados antes da invocação. Os três pontos em body ... correspondem a uma diretiva do compilador que indica que os itens de argumento na declaração de função devem ser copiados e colados nesse local.

Os motivos por trás de indicar explicitamente onde os argumentos da declaração callable pai devem ser copiados e colados são duplos: um, é desnecessário repetir a declaração de argumento e dois, garante que os functors que exigem argumentos adicionais, como o Controlled functor, possam ser introduzidos de maneira consistente.

Quando há exatamente uma especialização definindo a implementação do corpo padrão, o encapsulamento adicional do formulário body ... { <implementation> } pode ser omitido.

Recursão

Q# que podem ser recursivos direta ou indiretamente e podem ser declarados em qualquer ordem; uma operação ou função pode chamar a si mesma ou pode chamar outra chamada que chama direta ou indiretamente o chamador.

O espaço de pilha pode ser limitado ao ser executado em hardware quântico e as recursões que excedem esse limite de espaço de pilha resultam em um erro de runtime.