Declarações e Reatribuições de Variáveis
Os valores podem ser vinculados a símbolos usando as instruções let
e mutable
.
Esses tipos de ligações fornecem uma maneira conveniente de acessar um valor por meio da alça definida.
Apesar da terminologia enganosa emprestada de outros idiomas, identificadores declarados em um escopo local e contendo valores são chamados de variáveis .
Isso pode ser enganoso porque let
instruções definem identificadores de atribuição única, que são identificadores que permanecem vinculados ao mesmo valor durante a vigência de sua validade. As variáveis que podem ser religadas a valores diferentes em diferentes pontos do código precisam ser explicitamente declaradas como tal e são especificadas usando a instrução mutable
.
let var1 = 3;
mutable var2 = 3;
var2 = var2 + 1;
Neste exemplo, a instrução let
declara uma variável chamada var1
que não pode ser reatribuída e sempre contém o valor 3
. A instrução mutable
define uma variável var2
que está temporariamente vinculada ao valor 3
mas pode ser reatribuída a um valor diferente posteriormente usando uma expressão de atribuição, conforme mostrado na última linha. Você pode expressar a mesma afirmação com a versão mais curta var2 += 1;
, como é comum em outras línguas. Para obter mais informações, consulte Avaliar e reatribuir instruções.
Resumindo:
-
let
é usado para criar uma ligação imutável. -
mutable
é usado para criar uma ligação mutável. -
=
sem umlet
é usado para alterar o valor de uma ligação mutável.
Para as três afirmações, o lado esquerdo consiste em um símbolo ou uma tupla de símbolo. Se o lado direito da encadernação for uma tupla, então essa tupla pode ser total ou parcialmente desconstruída após a atribuição. O único requisito para a desconstrução é que a forma da tupla no lado direito corresponda à forma da tupla símbolo no lado esquerdo. A tupla de símbolo pode conter tuplas aninhadas ou símbolos omitidos, ou ambos, indicados por um sublinhado. Por exemplo:
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]
Para obter mais informações sobre a desconstrução usando o operador unwrap (!
), consulte Item access for struct types.
Todas as atribuições no Q# obedecem às mesmas regras de desconstrução, incluindo, por exemplo, alocações de qubit e atribuições de variáveis de loop.
Para ambos os tipos de ligações, os tipos das variáveis são inferidos do lado direito da ligação. O tipo de uma variável permanece sempre o mesmo, e uma expressão de atribuição não pode alterá-la.
As variáveis locais podem ser declaradas como mutáveis ou imutáveis. Existem algumas exceções, como variáveis de loop em loops for
, onde o comportamento é predefinido e não pode ser especificado.
Os argumentos de função e operação estão sempre imutavelmente ligados. Em combinação com a falta de tipos de referência, conforme discutido no tópico Immutabilidade, isso significa que uma função ou operação chamada nunca pode alterar quaisquer valores no lado do chamador.
Uma vez que os estados dos valores de Qubit
não são definidos ou observáveis a partir de Q#, isso não impede o acúmulo de efeitos colaterais quânticos, que são observáveis apenas através de medições. Para obter mais informações, consulte Tipos de dados quânticos.
Independentemente de como um valor é vinculado, os próprios valores são imutáveis. Em particular, isso é verdadeiro para matrizes e itens de matriz. Em contraste com as linguagens clássicas populares, onde as matrizes geralmente são tipos de referência, as matrizes em Q# - como todos os tipos - são tipos de valor e sempre imutáveis; ou seja, você não pode modificá-los após a inicialização. Alterar os valores acessados por variáveis de tipo de matriz requer construir explicitamente uma nova matriz e reatribuí-la ao mesmo símbolo. Para obter mais informações, consulte de imutabilidade e Copiar e atualizar expressões.
Avaliar e reatribuir instruções
As declarações da forma intValue += 1;
são comuns em muitas outras línguas. Aqui, intValue
precisa ser uma variável ligada mutavelmente do tipo Int
.
Tais instruções fornecem uma maneira conveniente de concatenação se o lado direito consiste na aplicação de um operador binário e o resultado é rebote para o argumento esquerdo do operador.
Por exemplo, este segmento de código
mutable counter = 0;
for i in 1 .. 2 .. 10 {
counter += 1;
// ...
}
incrementa o valor do contador counter
em cada iteração do loop de for
e é equivalente a
mutable counter = 0;
for i in 1 .. 2 .. 10 {
counter = counter + 1;
// ...
}
Existem declarações semelhantes para uma vasta gama de operadores de . Essas expressões de avaliação e reatribuição existem para todos os operadores em que o tipo da subexpressão mais à esquerda corresponde ao tipo de expressão. Mais precisamente, eles estão disponíveis para operadores binários lógicos e bitwise, incluindo deslocamento para a direita e para a esquerda, expressões aritméticas, incluindo exponenciação e módulo, e concatenações, bem como expressões de cópia e atualização.
O exemplo de função a seguir calcula a soma de uma matriz de números de 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;
}
Da mesma forma, a seguinte função multiplica cada item em uma matriz com o fator dado:
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;
}
Para obter mais informações, consulte Expressões contextuais, que contém outros exemplos em que expressões podem ser omitidas em um contexto específico quando uma expressão adequada pode ser inferida pelo compilador.