Freigeben über


Vorgänge und Funktionen

Wie in der Beschreibung des Qubit-Datentypsausführlicher erläutert, werden Quantenberechnungen in Form von Nebenwirkungen von Vorgängen ausgeführt, die nativ auf dem gezielten Quantenprozessor unterstützt werden. Dies sind in der Tat die einzigen Nebenwirkungen in Q#. Da alle Typen unveränderlichesind, gibt es keine Nebenwirkungen, die sich auf einen Wert auswirken, der explizit in Q#dargestellt wird. Solange eine Implementierung eines bestimmten aufrufbaren Vorgangs keines dieser systemeigenen implementierten Vorgänge direkt oder indirekt aufruft, erzeugt die Ausführung immer dieselbe Ausgabe, vorausgesetzt, die gleiche Eingabe.

Q# ermöglicht es Ihnen, solche rein deterministischen Berechnungen explizit in Funktionenaufzuteilen. Da der Satz systemeigener unterstützter Anweisungen nicht behoben und in die Sprache selbst integriert ist, sondern vollständig konfigurierbar und als Q# Bibliothek ausgedrückt wird, wird determinismus garantiert, indem erfordert, dass Funktionen nur andere Funktionen aufrufen und keine Vorgänge aufrufen können. Darüber hinaus werden systemeigene Anweisungen, die nicht deterministisch sind, d. h., weil sie den Quantenzustand beeinflussen, als Vorgänge dargestellt. Mit diesen beiden Einschränkungen können Funktionen ausgewertet werden, sobald ihr Eingabewert bekannt ist und grundsätzlich nie mehr als einmal für dieselbe Eingabe ausgewertet werden muss.

Q# unterscheidet daher zwischen zwei Arten von Aufrufablen: Vorgänge und Funktionen. Alle Aufrufe erfordern ein einzelnes Argument (potenziell Tupelwert) als Eingabe und erzeugen einen einzelnen Wert (Tupel) als Ausgabe. Syntaktisch wird der Vorgangstyp als <TIn> => <TOut> is <Char>ausgedrückt, wobei <TIn> durch den Argumenttyp ersetzt werden soll, <TOut> durch den Rückgabetyp ersetzt werden soll und <Char> durch die Vorgangsmerkmaleersetzt werden soll. Wenn keine Merkmale angegeben werden müssen, vereinfacht die Syntax die <TIn> => <TOut>. In ähnlicher Weise werden Funktionstypen als <TIn> -> <TOut>ausgedrückt.

Abgesehen von dieser Determinismusgarantie gibt es wenig Unterschied zwischen Vorgängen und Funktionen. Beide sind erstklassige Werte, die frei weitergegeben werden können; sie können als Rückgabewerte oder Argumente für andere aufrufbare Werte verwendet werden, wie im folgenden Beispiel gezeigt:

function Pow<'T>(op : 'T => Unit, pow : Int) : 'T => Unit {
    return PowImpl(op, pow, _);
}

Beide können basierend auf einer typparameterisierten Definition instanziiert werden, z. B. die typ parametrisierte Funktion Pow früher, und sie können teilweise wie in der return-Anweisung im Beispiel angewendet werden.

Vorgangseigenschaften

Zusätzlich zu den Informationen zum Eingabe- und Ausgabetyp enthält der Vorgangstyp Informationen zu den Merkmalen eines Vorgangs. In diesen Informationen wird beispielsweise beschrieben, welche Functors vom Vorgang unterstützt werden. Darüber hinaus enthält die interne Darstellung auch optimierungsrelevante Informationen, die vom Compiler abgeleitet werden.

Die Merkmale eines Vorgangs sind eine Reihe vordefinierter und integrierter Bezeichnungen. Sie werden in Form eines speziellen Ausdrucks ausgedrückt, der Teil der Typsignatur ist. Der Ausdruck besteht entweder aus einem der vordefinierten Beschriftungen oder einer Kombination von Merkmalenausdrücken über einen unterstützten binären Operator.

Es gibt zwei vordefinierte Sets, Adj und Ctl.

  • Adj ist der Satz, der eine einzelne Beschriftung enthält, die angibt, dass ein Vorgang angrenzend ist, d. h., er unterstützt den Adjoint Functor und die angewendete Quantentransformation kann "rückgängig gemacht" werden, d. h. es kann invertiert werden.
  • Ctl ist der Satz, der eine einzelne Beschriftung enthält, die angibt, dass ein Vorgang steuerbar ist, d. h. er unterstützt den Controlled Functor und seine Ausführung kann auf den Zustand anderer Qubits bedingt werden.

Die beiden Operatoren, die als Teil von Merkmalenausdrücken unterstützt werden, sind die festgelegte Union + und die festgelegte Schnittmenge *. In EBNF (Erweitertes Backus-Naur-Formular),

    predefined = "Adj" | "Ctl";
    characteristics = predefined 
        | "(", characteristics, ")" 
        | characteristics ("+"|"*") characteristics;

Wie erwartet, hat * eine höhere Priorität als + und beide sind linksassoziativ. Der Typ eines einheitlichen Vorgangs wird beispielsweise als <TIn> => <TOut> is Adj + Ctlausgedrückt, wobei <TIn> durch den Typ des Vorgangsarguments ersetzt werden soll, und <TOut> durch den Typ des zurückgegebenen Werts ersetzt werden.

Hinweis

Die Angabe der Merkmale eines Vorgangs in dieser Form hat zwei wesentliche Vorteile; Zum einen können neue Bezeichnungen eingeführt werden, ohne exponentiell viele Sprachstichwörter für alle Kombinationen von Bezeichnungen zu haben. Wichtiger ist, dass die Verwendung von Ausdrücken, um die Merkmale eines Vorgangs anzugeben, auch in Zukunft Parameterisierungen über Vorgangsmerkmale unterstützt.