Condividi tramite


Dichiarazioni chiamabili

Le dichiarazioni chiamabili o chiamabili, dichiarate in un ambito globale sono visibili pubblicamente per impostazione predefinita; ovvero possono essere usati ovunque nello stesso progetto e in un progetto che fa riferimento all'assembly in cui sono dichiarati. modificatori di Access consentono di limitare la visibilità solo all'assembly corrente, in modo che i dettagli di implementazione possano essere modificati in un secondo momento senza interrompere il codice che si basa su una libreria specifica.

Q# supporta due tipi di chiamabili: operazioni e funzioni. L'argomento operazioni e funzioni elaborato sulla distinzione tra i due. Q# supporta anche la definizione di modelli di ; ad esempio implementazioni con parametri di tipo per un determinato chiamabile. Per altre informazioni, vedere parametrizzazione del tipo .

Nota

Tali implementazioni con parametri di tipo non possono usare costrutti di linguaggio che si basano su proprietà specifiche degli argomenti di tipo; Attualmente non esiste alcun modo per esprimere vincoli di tipo in Q#o per definire implementazioni specializzate per argomenti di tipo specifici.

Chiamabili e funtori

Q# consente implementazioni specializzate per scopi specifici; Ad esempio, le operazioni in Q# possono definire in modo implicito o esplicito il supporto per determinati funtori e insieme alle implementazioni specializzate da richiamare quando viene applicato un funtore specifico a tale chiamabile.

Un funtore, in un certo senso, è una factory che definisce una nuova implementazione chiamabile che ha una relazione specifica con il chiamabile a cui è stato applicato. I funtori sono più delle tradizionali funzioni di livello superiore in quanto richiedono l'accesso ai dettagli di implementazione del chiamabile a cui sono stati applicati. In questo senso, sono simili ad altre factory, ad esempio modelli. Possono essere applicate anche a chiamabili con parametri di tipo.

Si consideri l'operazione seguente 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]));
            }
        }
    }

Questa operazione accetta un argomento di tipo Qubit[] e restituisce un valore di tipo Unit. L'annotazione is Adj + Ctl nella dichiarazione di ApplyQFT indica che l'operazione supporta sia il Adjoint che il functor Controlled. Per altre informazioni, vedere Caratteristiche dell'operazione). L'espressione Adjoint ApplyQFT accede alla specializzazione che implementa l'adiacente di ApplyQFTe Controlled ApplyQFT accede alla specializzazione che implementa la versione controllata di ApplyQFT. Oltre all'argomento dell'operazione originale, la versione controllata di un'operazione accetta una matrice di qubit di controllo e applica l'operazione originale sulla condizione in cui tutti questi qubit di controllo si trovano in uno stato |1⟩.

In teoria, un'operazione per cui è possibile definire una versione adiacente deve avere anche una versione controllata e viceversa. In pratica, tuttavia, potrebbe essere difficile sviluppare un'implementazione per una o l'altra, soprattutto per le implementazioni probabilistiche seguendo un modello di ripetizione fino al successo. Per questo motivo, Q# consente di dichiarare il supporto per ogni functor singolarmente. Tuttavia, poiché i due funtori commutano, un'operazione che definisce il supporto per entrambi deve avere anche un'implementazione (in genere definita in modo implicito, ovvero generata dal compilatore) per quando entrambi i funtori vengono applicati all'operazione.

Non esistono funtori che possono essere applicati alle funzioni. Le funzioni attualmente hanno esattamente un'implementazione del corpo e non sono presenti altre specializzazioni. Ad esempio, la dichiarazione

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

equivale a

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

In questo caso, body specifica che l'implementazione specificata si applica al corpo predefinito della funzione Hello, ovvero l'implementazione viene richiamata quando non sono stati applicati funtori o altri meccanismi factory prima della chiamata. I tre punti in body ... corrispondono a una direttiva del compilatore che indica che gli elementi dell'argomento nella dichiarazione di funzione devono essere copiati e incollati in questo punto.

I motivi alla base dell'indicazione esplicita della posizione in cui gli argomenti della dichiarazione chiamabile padre devono essere copiati e incollati sono due: uno, non è necessario ripetere la dichiarazione dell'argomento e due, garantisce che i funtori che richiedono argomenti aggiuntivi, ad esempio il funtore Controlled, possano essere introdotti in modo coerente.

Quando è presente esattamente una specializzazione che definisce l'implementazione del corpo predefinito, è possibile omettere il wrapping aggiuntivo del modulo body ... { <implementation> }.

Ricorsione

Q# i chiamabili possono essere direttamente o indirettamente ricorsivi e possono essere dichiarati in qualsiasi ordine; un'operazione o una funzione può chiamare se stessa oppure può chiamare un altro chiamabile direttamente o indirettamente chiama il chiamante.

Lo spazio dello stack può essere limitato durante l'esecuzione su hardware quantistico e le ricorsioni che superano tale limite di spazio dello stack generano un errore di runtime.