Udostępnij za pośrednictwem


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.