Variable Deklarationen und Neuzuweisungen
Werte können mithilfe der let
- und mutable
-Anweisungen an Symbole gebunden werden.
Diese Arten von Bindungen bieten eine bequeme Möglichkeit, über den definierten Handle auf einen Wert zuzugreifen.
Trotz der irreführenden Terminologie, die von anderen Sprachen ausgeliehen wurde, werden Handles, die in einem lokalen Bereich deklariert sind und Werte enthalten, Variablengenannt.
Dies kann irreführend sein, da let
Anweisungen Handle mit einer einzelnen Zuordnungdefinieren, bei denen es sich um Handles handelt, die für die Gültigkeitsdauer an denselben Wert gebunden bleiben. Variablen, die an unterschiedliche Werte an verschiedenen Stellen im Code neu gebunden werden können, müssen explizit als solche deklariert werden und mithilfe der mutable
-Anweisung angegeben werden.
let var1 = 3;
mutable var2 = 3;
var2 = var2 + 1;
In diesem Beispiel deklariert die let
-Anweisung eine Variable mit dem Namen var1
, die nicht neu zugewiesen werden kann und immer den Wert 3
enthält. Die mutable
-Anweisung definiert eine Variable var2
, die vorübergehend an den Wert 3
gebunden ist, später jedoch einem anderen Wert zugewiesen werden kann, wie in der letzten Zeile dargestellt. Sie können dieselbe Anweisung mit der kürzeren Version var2 += 1;
ausdrücken, wie es in anderen Sprachen üblich ist. Weitere Informationen finden Sie unter Evaluate and reassign statements.
Zusammenfassung:
-
let
wird verwendet, um eine unveränderliche Bindung zu erstellen. -
mutable
wird verwendet, um eine änderbare Bindung zu erstellen. -
=
ohnelet
wird verwendet, um den Wert einer änderbaren Bindung zu ändern.
Für alle drei Anweisungen besteht die linke Seite aus einem Symbol oder einem Symbol-Tupel. Wenn es sich bei der rechten Seite der Bindung um ein Tupel handelt, kann dieses Tupel bei der Zuordnung vollständig oder teilweise dekonstruiert werden. Die einzige Anforderung für die Dekonstruktion besteht darin, dass die Form des Tupels auf der rechten Seite mit der Form des Symbol-Tupels auf der linken Seite übereinstimmt. Das Symbol-Tupel kann geschachtelte Tupel oder ausgelassene Symbole oder beides enthalten, die durch einen Unterstrich gekennzeichnet sind. Zum Beispiel:
let (a, (_, b)) = (1, (2, 3)); // a is bound to 1, b is bound to 3
mutable (x, y) = ((1, 2), [3, 4]); // x is bound to (1, 2), y is bound to [3, 4]
(x, _, y) = ((5, 6), 7, [8]); // x is re-bound to (5,6), y is re-bound to [8]
Weitere Informationen zur Dekonstruktion mithilfe des Unwrap (!
) -Operators finden Sie unter Elementzugriff für Strukturtypen.
Alle Zuordnungen in Q# entsprechen den gleichen Dekonstruktionsregeln, z. B. Qubit-Zuordnungen und Schleifenvariablenzuweisungen.
Bei beiden Arten von Bindungen werden die Variablentypen von der rechten Seite der Bindung abgeleitet. Der Typ einer Variablen bleibt immer gleich, und ein Zuordnungsausdruck kann ihn nicht ändern.
Lokale Variablen können entweder als änderbar oder unveränderlich deklariert werden. Es gibt einige Ausnahmen, z. B. Schleifenvariablen in for
Schleifen, wobei das Verhalten vordefiniert ist und nicht angegeben werden kann.
Funktions- und Vorgangsargumente sind immer unveränderlich gebunden. In Kombination mit dem Mangel an Bezugstypen, wie im Thema Unveränderlichkeit erläutert, bedeutet dies, dass eine aufgerufene Funktion oder ein Vorgang niemals Werte auf der Aufruferseite ändern kann.
Da die Zustände von Qubit
Werten nicht innerhalb von Q#definiert oder feststellbar sind, schließt dies nicht die Ansammlung von Quanten-Nebenwirkungen aus, die nur über Messungen feststellbar sind. Weitere Informationen finden Sie unter Quantum-Datentypen.
Unabhängig davon, wie ein Wert gebunden ist, sind die Werte selbst unveränderlich. Dies gilt insbesondere für Arrays und Arrayelemente. Im Gegensatz zu beliebten klassischen Sprachen, bei denen Arrays häufig Referenztypen sind, sind Arrays in Q# - wie alle Typen - Werttypen und immer unveränderlich; Das heißt, Sie können sie nach der Initialisierung nicht mehr ändern. Das Ändern der Werte, auf die von Arraytypvariablen zugegriffen wird, erfordert daher das explizite Erstellen eines neuen Arrays und das erneute Zuweisen des selben Symbols. Weitere Informationen finden Sie unter Unveränderlichkeit und Kopieren und Aktualisieren von Ausdrücken.
Evaluate-and-reassign-Anweisungen
Anweisungen der Form intValue += 1;
sind in vielen anderen Sprachen üblich. Hier muss intValue
eine änderbare Variable vom Typ Int
sein.
Solche Anweisungen bieten eine bequeme Methode der Verkettung, wenn die rechte Seite aus dem Anwenden eines binären Operators besteht und das Ergebnis an das linke Argument des Operators gebunden ist.
Beispiel: dieses Codesegment
mutable counter = 0;
for i in 1 .. 2 .. 10 {
counter += 1;
// ...
}
erhöht den Wert des Zählers counter
in jeder Iteration der for
Schleife und entspricht
mutable counter = 0;
for i in 1 .. 2 .. 10 {
counter = counter + 1;
// ...
}
Ähnliche Anweisungen sind für eine vielzahl von Operatorenvorhanden. Solche Auswertungs- und Neuzuweisungsausdrücke sind für alle Operatoren vorhanden, bei denen der Typ des am linken Randausdrucks mit dem Ausdruckstyp übereinstimmt. Genauer gesagt sind sie für binäre logische und bitweise Operatoren verfügbar, darunter rechte und linke Schicht, arithmetische Ausdrücke einschließlich Exponentiation und Modul sowie Verkettungen sowie Copy-and-Update-Ausdrücke.
Im folgenden Funktionsbeispiel wird die Summe eines Arrays von Complex
Zahlen berechnet:
function ComplexSum(values : Complex[]) : Complex {
mutable res = Complex(0., 0.);
for complex in values {
res = new Complex { Re = res.Re + complex.Re, Im = res.Im + complex.Im };
}
return res;
}
Entsprechend multipliziert die folgende Funktion jedes Element in einem Array mit dem angegebenen Faktor:
function Multiplied(factor : Double, array : Double[]) : Double[] {
mutable res = new Double[Length(array)];
for i in IndexRange(res) {
res w/= i <- factor * array[i];
}
return res;
}
Weitere Informationen finden Sie unter Kontextausdrücke, die andere Beispiele enthält, in denen Ausdrücke in einem bestimmten Kontext weggelassen werden können, wenn ein geeigneter Ausdruck vom Compiler abgeleitet werden kann.