To reduce the need for mutable bindings, Q# supports copy-and-update expressions for value types with item access. User defined types and arrays both are immutable and fall into this category. User defined types allow to access items via name, whereas arrays allow to access items via an index or range of indices.
Copy-and-update expressions instantiate a new value of with all items set to the corresponding value in the original expression, except certain specified items(s), which are set to the one(s) defined on the right hand side of the expression.
They are constructed using a ternary operator
<-; the syntax
w/ should be read as the commonly used short notation for "with":
original w/ itemAccess <- modification
original is either an expression of user defined type or an array expression. The corresponding requirements for
modification are specified in the corresponding subsection below.
In terms of precedence, the copy-and-update operator is left-associative and has lowest precedence,
and in particular lower precedence than the range operator (
..) or the ternary conditional operator (
The chosen left associativity allows easy chaining of copy-and-update expressions:
let model = Default<SequentialModel>() w/ Structure <- ClassifierStructure() w/ Parameters <- parameters w/ Bias <- bias;
Like for any operator that constructs an expression that is of the same type as the left-most expression involved, the corresponding evaluate-and-reassign statement is available.
The two statements below for example achieve the following: The first statement declares a mutable variable
arr and binds it to the default value of an integer array. The second statement then builds a new array with the first item (with index 0) set to 10, and reassigns it to
mutable arr = new Int; // arr contains [0,0,0] set arr w/= 0 <- 10; // arr contains [10,0,0]
The second statement is nothing but a short-hand for the more verbose syntax
set arr = arr w/ 0 <- 10;.
Copy-and-Update of User Defined Types
If the value
original is of user defined type, then
itemAccess denotes the name of the item that diverges from the original value. The reason that this is not simply another expression, like
modification, is that the ability to simply use the item name without any further qualification is limited to this context; it is one of two contextual expressions in Q#.
The type of the
modification expression needs to match the type of the named item that diverges.
For instance, if
complex contains the value
Complex(0., 0.), where the type
Complex is defined here, then
complex w/ Re <- 1.
is an expression of type
Complex that evaluates to
Copy-and-Update of Arrays
itemAccess can be an arbitrary expression of a suitable type;
the same types that are valid for array slicing are valid in this context. Concretely, the
itemAccess expression can be of type
itemAccess is a value of type
Int, then the type of
modification has to match the item type of the array. If
itemAccess is a value of type
Range then the type of
modification has to be the same as the array type.
For example, if
arr contains an array
arr w/ 0 <- 10is the array
arr w/ 2 <- 10is the array
arr w/ 0..2..3 <- [10,12]is the array
Copy-and-update expressions allow efficient creation of new arrays based on existing ones. The implementation for copy-and-update expressions avoids copying the entire array but merely duplicates the necessary parts to achieve the desired behavior, and performs an in-place modification if possible. Array initializations hence do not incur additional overhead due to immutability.
Microsoft.Quantum.Arrays namespace provides and arsenal of convenient tools for array creation and manipulation.
For instance, the function
ConstantArray creates an array of the specified length and initializes each item to a given value.
Copy-and-update expressions are a convenient way to construct new arrays on the fly;
the following expression, e.g., evaluates to an array with all items set to
PauliI, except the item at index
i, which is set to
ConstantArray(n, PauliI) w/ i <- PauliZ