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 uchwytu. Pomimo wprowadzającej w błąd terminologii zapożyczonej z innych języków, obsługa zadeklarowana w zakresie lokalnym i zawierająca wartości są nazywane zmiennymi . To nazewnictwo może być mylące, ponieważ let instrukcje definiują dojścia pojedynczego przypisania, które są obsługiwane, 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 kodu, muszą być jawnie zadeklarowane jako takie i są określane przy użyciu instrukcji mutable.

    let var1 = 3; 
    mutable var2 = 3; 
    var2 = var2 + 1; 

W tym przykładzie instrukcja let deklaruje zmienną o nazwie var1, która nie może zostać ponownie przypisana i zawsze zawiera wartość 3. Instrukcja mutable definiuje zmienną var2, która jest tymczasowo powiązana z 3 wartości, ale może zostać ponownie przypisana do innej wartości później przy użyciu wyrażenia przypisania, jak pokazano w ostatnim wierszu. Tę samą instrukcję można wyrazić przy użyciu krótszej wersji var2 += 1;, co jest wspólne w innych językach. Aby uzyskać więcej informacji, zobacz Evaluate and reassign statements.

Podsumowanie:

  • let służy do tworzenia niezmiennego powiązania.
  • mutable służy do tworzenia powiązania modyfikowalnego.
  • = bez let 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 do 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 albo 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]
(x, _, y) = ((5, 6), 7, [8]);  // x is re-bound to (5,6), y is re-bound to [8]

Aby uzyskać więcej informacji na temat dekonstrukcji przy użyciu operatora unwrap (!), zobacz Item access for struct types.

Wszystkie przypisania w Q# przestrzegają tych samych reguł dekonstrukcji, w tym na przykład alokacji kubitów i przypisań 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 wyrażenie przypisania nie może go zmienić. Zmienne lokalne można zadeklarować jako modyfikowalne lub niezmienne. Istnieją pewne wyjątki, takie jak zmienne pętli w pętlach for, 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ść, niezmiennie powiązana oznacza, że wywoływana funkcja lub operacja nigdy nie może zmienić żadnych wartości po stronie obiektu wywołującego.

Ponieważ stany wartości Qubit nie są definiowane ani zauważalne z poziomu Q#, nie wyklucza to akumulowania efektów ubocznych kwantowych, które można zaobserwować tylko za pomocą pomiarów. Aby uzyskać więcej informacji, zobacz Quantum data types.

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# — 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 wyrażenia kopiowania i aktualizowania .

Instrukcje evaluate-and-reassign

Instrukcje formularza intValue += 1; są wspólne w wielu innych językach. W tym miejscu intValue musi być niezmienną zmienną typu Int. Takie instrukcje zapewniają wygodny sposób łączenia, jeśli po prawej stronie składa się z zastosowania operatora binarnego, a wynik jest ponownie powiązany z lewym argumentem operatora. Na przykład ten segment kodu

mutable counter = 0;
for i in 1 .. 2 .. 10 {
    counter += 1;
    // ...
}

zwiększa wartość licznika counter w każdej iteracji pętli for i jest równoważna

mutable counter = 0;
for i in 1 .. 2 .. 10 {
    counter = counter + 1;
    // ...
}

Podobne instrukcje istnieją dla szerokiego zakresu operatorów . Takie wyrażenia evaluate-and-reassign istnieją dla wszystkich operatorów, dla których typ podexpressionu po lewej stronie jest zgodny z typem wyrażenia. Mówiąc dokładniej, są 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ów oraz łączenia, a także wyrażeń kopiowania i aktualizacji.

Poniższy przykład funkcji oblicza sumę tablicy liczb Complex:

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;
}

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) {
        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.