Deklaracje zmiennych i ponowne przypisania
Wartości mogą być powiązane z symbolami przy użyciu instrukcji let
i mutable
.
Tego rodzaju powiązania zapewniają wygodny sposób uzyskiwania dostępu do wartości za pośrednictwem zdefiniowanego dojścia.
Pomimo mylącej terminologii zapożyczonej z innych języków, obsługa zadeklarowana w zakresie lokalnym i zawierająca wartości są nazywane zmiennymi.
Może to być mylące, ponieważ let
instrukcje definiują dojścia do pojedynczego przypisania, które pozostają powiązane z tą samą wartością przez czas ich ważności. Zmienne, które mogą być ponownie powiązane z różnymi wartościami w różnych punktach w kodzie, muszą być jawnie zadeklarowane jako takie i są określane przy użyciu instrukcji mutable
.
let var1 = 3;
mutable var2 = 3;
set var2 = var2 + 1;
W tym przykładzie let
instrukcja deklaruje zmienną o nazwie var1
, której nie można ponownie przypisać i zawsze zawiera wartość 3
. Instrukcja mutable
definiuje zmienną var2
, która jest tymczasowo powiązana z wartością 3
, ale może zostać ponownie przypisana do innej wartości później przy użyciu set
instrukcji , jak pokazano w ostatnim wierszu. Tę samą instrukcję można wyrazić przy użyciu krótszej wersji set var2 += 1;
, co jest wspólne w innych językach. Aby uzyskać więcej informacji, zobacz Evaluate and reassign statements (Ocena i ponowne przypisywanie instrukcji).
Podsumowując:
-
let
służy do tworzenia niezmiennego powiązania. -
mutable
służy do tworzenia powiązania modyfikowalnego. -
set
służy do zmiany wartości powiązania modyfikowalnego.
Dla wszystkich trzech instrukcji lewa strona składa się z symbolu lub krotki symbolu. Jeśli prawa strona powiązania jest krotką, ta krotka może być w pełni lub częściowo zdekonstrukowana po przypisaniu. Jedynym wymaganiem dekonstrukcji jest to, że kształt krotki po prawej stronie pasuje do kształtu krotki symboli po lewej stronie. Krotka symboli może zawierać zagnieżdżone krotki lub pominięte symbole lub oba te symbole, wskazywane przez podkreślenie. Na przykład:
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]
set (x, _, y) = ((5, 6), 7, [8]); // x is re-bound to (5,6), y is re-bound to [8]
Wszystkie przypisania w Q# programie są zgodne z tymi samymi regułami dekonstrukcji, w tym na przykład alokacjami kubitów i przypisaniami zmiennych pętli.
W przypadku obu rodzajów powiązań typy zmiennych są wnioskowane z prawej strony powiązania. Typ zmiennej zawsze pozostaje taki sam, a set
instrukcja nie może jej zmienić.
Zmienne lokalne można zadeklarować jako modyfikowalne lub niezmienne. Istnieją pewne wyjątki, takie jak zmienne pętli w for
pętlach, w których zachowanie jest wstępnie zdefiniowane i nie można go określić.
Argumenty funkcji i operacji są zawsze niezmiennie powiązane. W połączeniu z brakiem typów odwołań, jak opisano w temacie Niezmienność , oznacza to, że wywołana funkcja lub operacja nigdy nie może zmienić żadnych wartości po stronie obiektu wywołującego.
Ponieważ stany Qubit
wartości nie są zdefiniowane lub obserwowalne z poziomu Q#elementu , nie wyklucza to gromadzenia efektów ubocznych kwantowych, które można zaobserwować tylko za pomocą pomiarów. Aby uzyskać więcej informacji, zobacz Typy danych kwantowych.
Niezależnie od tego, jak wartość jest powiązana, same wartości są niezmienne. W szczególności dotyczy to tablic i elementów tablic. W przeciwieństwie do popularnych języków klasycznych, w których tablice często są typami referencyjnymi, tablice w Q# obiekcie — podobnie jak wszystkie typy — są typami wartości i zawsze niezmiennymi; oznacza to, że nie można ich modyfikować po zainicjowaniu. Zmiana wartości uzyskiwanych przez zmienne typu tablicy wymaga zatem jawnego skonstruowania nowej tablicy i ponownego przypisania jej do tego samego symbolu. Aby uzyskać więcej informacji, zobacz Niezmienność i Kopiowanie i aktualizowanie wyrażeń.
Instrukcje evaluate-and-reassign
Instrukcje formularza set intValue += 1;
są wspólne w wielu innych językach.
intValue
Tutaj musi być niezmiennie powiązana zmienna typu Int
.
Takie instrukcje zapewniają wygodny sposób łączenia, jeśli po prawej stronie składa się z zastosowania operatora binarnego, a wynik jest odbijany do lewego argumentu operatora.
Na przykład ten segment kodu
mutable counter = 0;
for i in 1 .. 2 .. 10 {
set counter += 1;
// ...
}
zwiększa wartość licznika counter
w każdej iteracji for
pętli i jest równoważna
mutable counter = 0;
for i in 1 .. 2 .. 10 {
set counter = counter + 1;
// ...
}
Podobne instrukcje istnieją dla szerokiego zakresu operatorów. Słowo set
kluczowe w takich instrukcjach evaluate-and-reassign musi być po nim pojedyncza zmienna modyfikowalna, która jest wstawiana jako wyrażenie podrzędne po lewej stronie przez kompilator.
Takie instrukcje evaluate-and-reassign istnieją dla wszystkich operatorów, w których typ wyrażenia podrzędnego po lewej stronie jest zgodny z typem wyrażenia.
Mówiąc dokładniej, są one dostępne dla binarnych operatorów logicznych i bitowych, w tym prawego i lewego przesunięcia, wyrażeń arytmetycznych, w tym wykładników i modul oraz łączenia, a także wyrażeń kopiowania i aktualizacji.
Poniższy przykład funkcji oblicza sumę tablicy Complex
liczb:
function ComplexSum(values : Complex[]) : Complex {
mutable res = Complex(0., 0.);
for complex in values {
set res w/= Re <- res::Re + complex::Re;
set res w/= Im <- res::Im + complex::Im;
}
return res;
}
Podobnie następująca funkcja mnoży każdy element w tablicy przy użyciu danego czynnika:
function Multiplied(factor : Double, array : Double[]) : Double[] {
mutable res = new Double[Length(array)];
for i in IndexRange(res) {
set res w/= i <- factor * array[i];
}
return res;
}
Aby uzyskać więcej informacji, zobacz Wyrażenia kontekstowe, które zawierają inne przykłady, w których wyrażenia można pominąć w określonym kontekście, gdy odpowiednie wyrażenie może zostać wywnioskowane przez kompilator.