Déclarations appelables

Les déclarations callables, ou callables, déclarées dans une étendue globale sont visibles publiquement par défaut ; Autrement dit, ils peuvent être utilisés n’importe où dans le même projet et dans un projet qui fait référence à l’assembly dans lequel ils sont déclarés. Les modificateurs d’accès vous permettent de limiter leur visibilité à l’assembly actuel uniquement, de sorte que les détails de l’implémentation peuvent être modifiés ultérieurement sans interrompre le code qui s’appuie sur une bibliothèque spécifique.

Q# prend en charge deux types de callables : les opérations et les fonctions. La rubrique Operations and Functions détaille la distinction entre les deux. Q# prend également en charge la définition de modèles. Par exemple, les implémentations de type paramétrable pour un appelable défini. Pour plus d’informations, consultez Paramétrages de types.

Notes

Ces implémentations de type-paramétrée ne peuvent pas utiliser de constructions de langage qui reposent sur des propriétés particulières des arguments de type. Il n’existe actuellement aucun moyen d’exprimer des contraintes de type dans Q#, ni de définir des implémentations spécialisées pour des arguments de type particuliers.

Callables et functors

Q# permet des implémentations spécialisées à des fins spécifiques. Par exemple, les opérations dans Q# peuvent définir implicitement ou explicitement la prise en charge de certains functors, ainsi que les implémentations spécialisées à appeler lorsqu’un functor spécifique est appliqué à cet appelable.

Un functor, en un sens, est une fabrique qui définit une nouvelle implémentation pouvant être appelée qui a une relation spécifique avec l’appelable à laquelle elle a été appliquée. Les functors sont plus que des fonctions de niveau supérieur traditionnelles, car elles nécessitent l’accès aux détails d’implémentation de l’appelable auquel elles ont été appliquées. Dans ce sens, elles sont similaires aux autres fabriques, comme les modèles. Ils peuvent également être appliqués aux callables paramétrables de type.

Considérez l’opération suivante : 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]));
            }
        }
    }

Cette opération prend un argument de type Qubit[] et retourne une valeur de type Unit. L’annotation is Adj + Ctl dans la déclaration de ApplyQFT indique que l’opération prend en charge à la fois le Adjoint et le functor Controlled. (Pour plus d’informations, consultez Caractéristiques de l’opération). L’expression Adjoint ApplyQFT accède à la spécialisation qui implémente l’adjoint de ApplyQFTet Controlled ApplyQFT accède à la spécialisation qui implémente la version contrôlée de ApplyQFT. En plus de l’argument de l’opération d’origine, la version contrôlée d’une opération prend un tableau de qubits de contrôle et applique l’opération d’origine à la condition que tous ces qubits de contrôle soient dans un état |1⟩.

En théorie, une opération pour laquelle une version voisine peut être définie doit également avoir une version contrôlée et inversement. Toutefois, dans la pratique, il peut être difficile de développer une implémentation pour l’une ou l’autre, en particulier pour les implémentations probabilistes qui suivent un modèle de répétition jusqu’à la réussite. Pour cette raison, Q# vous permet de déclarer individuellement la prise en charge de chaque functor. Toutefois, étant donné que les deux functors s’activent, une opération qui définit la prise en charge des deux éléments doit également avoir une implémentation (généralement définie implicitement, ce qui signifie qu’elle est générée par le compilateur) lorsque les deux functors sont appliqués à l’opération.

Il n’existe aucun foncteur qui peut être appliqué aux fonctions. Les fonctions ont actuellement exactement une implémentation de corps et aucune spécialisation supplémentaire. Par exemple, la déclaration

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

équivaut à :

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

Ici, body spécifie que l’implémentation donnée s’applique au corps par défaut de la fonction Hello, ce qui signifie que l’implémentation est appelée quand aucun functor ou autre mécanisme de fabrique n’a été appliqué avant l’appel. Les trois points de body ... correspondent à une directive de compilateur indiquant que les éléments d’arguments dans la déclaration de fonction doivent être copiés et collés à cet endroit.

Les raisons qui expliquent explicitement où les arguments de la déclaration pouvant être appelée parent doivent être copiés et collés sont deux raisons : une, il n’est pas nécessaire de répéter la déclaration d’argument, et deux, elle garantit que les foncteurs qui nécessitent des arguments supplémentaires, comme le Controlled foncteur, peuvent être introduits de manière cohérente.

Lorsqu’il existe exactement une spécialisation définissant l’implémentation du corps par défaut, l’habillage supplémentaire du formulaire body ... { <implementation> } peut être omis.

Récursivité

Les callables Q# peuvent être récursifs directement ou indirectement et peuvent être déclarés dans n’importe quel ordre. Une opération ou une fonction peut ainsi s’appeler elle-même ou peut appeler un autre callable qui appelle directement ou indirectement l’appelant.

En cas d’exécution sur du matériel quantique, l’espace de pile peut être limité, et les récurrences qui dépassent cette limite d’espace de pile entraînent une erreur d’exécution.