17.3 Multiple Indexing on the Left of Assignments
Multiple expressions may be used on the left-hand side of an assignment statement, where they work exactly the same way as on the right-hand side in terms of picking out entries of a container. For example, consider the following.
int a[3];
int c[2];
int idxs[2];
... // define: a == (1, 2, 3); c == (5, 9)
// idxs = (3,2)
a[idxs] = c; // result: a == (1, 9, 5)
The result above can be worked out by noting that the assignment sets a[idxs[1]]
(a[3]
) to c[1]
(5
) and a[idxs[2]]
(a[2]
) to c[2]
(9
).
The same principle applies when there are many multiple indexes, as in the following example.
int a[5, 7];
int c[2, 2];
...
a[2:3, 5:6] = c; // result: a[2, 5] == c[1, 1]; a[2, 6] == c[1, 2]
// a[3, 5] == c[2, 1]; a[3, 6] == c[2, 2]
As in the one-dimensional case, the right-hand side is written into the slice, block, or general chunk picked out by the left-hand side.
Usage on the left-hand side allows the full generality of multiple indexing, with single indexes reducing dimensionality and multiple indexes maintaining dimensionality while rearranging, slicing, or blocking. For example, it is valid to assign to a segment of a row of an array as follows.
int a[10, 13];
int c[2];
...
a[4, 2:3] = c; // result: a[4, 2] == c[1]; a[4, 3] == c[2]
Assign-by-Value and Aliasing
Aliasing issues arise when there are references to the same data structure on the right-hand and left-hand side of an assignment. For example, consider the array a
in the following code fragment.
int a[3];
... // define: a == (5, 6, 7)
a[2:3] = a[1:2];
... // result: a == (5, 5, 6)
The reason the value of a
after the assignment is \((5,5,6)\) rather than \((5,5,5)\) is that Stan behaves as if the right-hand side expression is evaluated to a fresh copy. As another example, consider the following.
int a[3];
int idxs[3];
... // define idxs = (2, 1, 3)
a[idxs] = a;
In this case, it is evident why the right-hand side needs to be copied before the assignment.
It is tempting (but wrong) to think of the assignment a[2:3] = a[1:2]
as executing the following assignments.
... // define: a = (5, 6, 7)
a[2] = a[1]; // result: a = (5, 5, 7)
a[3] = a[2]; // result: a = (5, 5, 5)!
This produces a different result than executing the assignment because a[2]
’s value changes before it is used.