Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Aufrufbare Deklarationen oder aufrufbare, die in einem globalen Bereich deklariert sind, sind standardmäßig öffentlich sichtbar; Das heißt, sie können an einer beliebigen Stelle im selben Projekt und in einem Projekt verwendet werden, das auf die Assembly verweist, in der sie deklariert werden. Access-Modifizierer ermöglichen es Ihnen, ihre Sichtbarkeit nur auf die aktuelle Assembly einzuschränken, sodass Implementierungsdetails später geändert werden können, ohne Code zu unterbrechen, der auf einer bestimmten Bibliothek basiert.
Q# unterstützt zwei Arten von Aufrufablen: Vorgänge und Funktionen. Das Thema Operations and Functions erläutert die Unterscheidung zwischen den beiden. Q# unterstützt auch das Definieren von Vorlagen; Beispiel: typparameterisierte Implementierungen für einen bestimmten aufrufbaren Wert. Weitere Informationen finden Sie unter Typparameterisierungen.
Hinweis
Solche typ parametrisierten Implementierungen dürfen keine Sprachkonstrukte verwenden, die auf bestimmten Eigenschaften der Typargumente basieren; Es gibt derzeit keine Möglichkeit, Typeinschränkungen in Q#auszudrücken oder spezielle Implementierungen für bestimmte Typargumente zu definieren.
Aufrufbare und Functors
Q# ermöglicht spezielle Implementierungen für bestimmte Zwecke; Beispielsweise können Vorgänge in Q# implizite oder explizite Unterstützung für bestimmte Functorsdefinieren und zusammen mit den speziellen Implementierungen aufrufen, die aufgerufen werden sollen, wenn ein bestimmter Functor auf diese aufrufbar angewendet wird.
Ein Functor ist in einem Sinne eine Factory, die eine neue aufrufbare Implementierung definiert, die eine bestimmte Beziehung zu der aufrufbaren Implementierung aufweist, auf die sie angewendet wurde. Functors sind mehr als herkömmliche Funktionen auf höherer Ebene, da sie Zugriff auf die Implementierungsdetails der aufrufbaren Funktionen benötigen, auf die sie angewendet wurden. In diesem Sinne ähneln sie anderen Fabriken, z. B. Vorlagen. Sie können auch auf typparameterisierte Aufrufables angewendet werden.
Betrachten Sie den folgenden Vorgang, 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]));
}
}
}
Dieser Vorgang verwendet ein Argument vom Typ Qubit[]
und gibt einen Wert vom Typ Unit
zurück. Die Anmerkung is Adj + Ctl
in der Deklaration von ApplyQFT
gibt an, dass der Vorgang sowohl den Adjoint
als auch den Controlled
-Functor unterstützt. (Weitere Informationen finden Sie unter Eigenschaften des Vorgangs). Der Ausdruck Adjoint ApplyQFT
greift auf die Spezialisierung zu, die den angrenzenden ApplyQFT
implementiert, und Controlled ApplyQFT
greift auf die Spezialisierung zu, die die kontrollierte Version von ApplyQFT
implementiert.
Zusätzlich zum Argument des ursprünglichen Vorgangs verwendet die kontrollierte Version eines Vorgangs ein Array von Steuerelement-Qubits und wendet den ursprünglichen Vorgang auf die Bedingung an, dass sich alle diese Steuerelement-Qubits in einem |1⟩-Zustand befinden.
Theoretisch sollte ein Vorgang, für den eine angrenzende Version definiert werden kann, auch eine kontrollierte Version aufweisen und umgekehrt. In der Praxis kann es jedoch schwierig sein, eine Implementierung für eine oder die andere zu entwickeln, insbesondere für probabilistische Implementierungen nach einem Wiederholungs-bis-Erfolg-Muster. Aus diesem Grund können Sie Q# die Unterstützung für jeden Functor einzeln deklarieren. Da die beiden Functors jedoch pendeln, muss ein Vorgang, der die Unterstützung für beide definiert, auch eine Implementierung (in der Regel implizit definiert, was compilergeneriert ist) aufweisen, wenn beide Functors auf den Vorgang angewendet werden.
Es gibt keine Funktoren, die auf Funktionen angewendet werden können. Funktionen verfügen derzeit über genau eine Bodyimplementierung und keine weiteren Spezialisierungen. Beispiel: die Deklaration
function Hello (name : String) : String {
$"Hello, {name}!"
}
ist gleichbedeutend mit
function Hello (name : String) : String {
body ... {
$"Hello, {name}!"
}
}
Hier gibt body
an, dass die angegebene Implementierung auf den Standardtext der Funktion Hello
angewendet wird, was bedeutet, dass die Implementierung aufgerufen wird, wenn vor dem Aufruf keine Functors oder andere Factorymechanismen angewendet wurden. Die drei Punkte in body ...
entsprechen einer Compilerdirektive, die angibt, dass die Argumentelemente in der Funktionsdeklaration kopiert und an diese Stelle eingefügt werden sollen.
Die Gründe für explizite Angabe, wo die Argumente der übergeordneten aufrufbaren Deklaration kopiert und eingefügt werden sollen, sind zweifach: eine, es ist unnötig, die Argumentdeklaration zu wiederholen, und zwei, es stellt sicher, dass Functors, die zusätzliche Argumente erfordern, wie der Controlled
Functor, auf konsistente Weise eingeführt werden können.
Wenn genau eine Spezialisierung die Implementierung des Standardtexts definiert, wird möglicherweise der zusätzliche Umbruch des Formulars body ... { <implementation> }
weggelassen.
Rekursion
Q# aufrufbare Elemente können direkt oder indirekt rekursiv sein und in beliebiger Reihenfolge deklariert werden; ein Vorgang oder eine Funktion kann sich selbst aufrufen oder einen anderen aufrufbaren Aufruf ausführen, der den Aufrufer direkt oder indirekt aufruft.
Stapelspeicher können begrenzt sein, wenn sie auf Quantenhardware ausgeführt werden, und Rekursionen, die diese Stapelspeichergrenze überschreiten, führen zu einem Laufzeitfehler.