20409, "jeremiah-corrado", "[Feature Request] Array access shorthand for endpoints of a Domain", "2022-08-08T23:39:08Z"
Proposal
Introduce a new symbol into the array access syntax which acts as a shorthand for either the low
or high
index along the relevant dimension.
The new symbol could be used both in direct array access, i.e. retrieving a single value or dimension, and array slicing using ranges, i.e. getting a range of values along a dimension.
In the following examples and explanation I'll use the carrot (^
) symbol; however I'd be open to swapping it for something else. (There is a potential for confusion when the endpoints of a range are defined in terms of some exponential expression like: 0..5^-2
, but I don't think there would be a parsing/syntax conflict)
Syntax examples
The details of this proposal are best explained using examples. The following should be pretty comprehensive:
// set all but the first and last elements of an array to zero
a[^+1..^-1] = 0;
// set an inner slice to zero (ranging from the 4th element to the 7th element from the end)
a[^+4..^-7] = 0;
// set the 5th element of an array to zero
a[^+5] = 0;
// set the first and last elements to zero (respectively)
a[^+] = 0;
a[^-] = 0;
// set the second to last column of a 2D array to zero
a[.., ^-2] = 0;
// swap the first and last rows of a 2D array
a[^+, ..] <=> a[^-, ..]
In the above examples, the ^-
syntax (to index from the end of an array) is of course more useful, as ^+
is standing in for 0
(assuming the array indexing is zero-based). However, I think it would be beneficial to include both for the sake of completeness, and because ^+
would allow users to write code which is more generic over different starting indices.
The syntax for indexing into the entire array would technically be expanded to include the following; however, I doubt these would be used in practice:
a[^+..^-] = 0;
a[^+..] = 0;
a[..^-] = 0;
Motivating Example
Recently, a realistic example of how this could be useful came up while porting some Finite Difference code from Python to Chapel. The following Python function sets the normal "derivative" to zero on the border of a 2D numpy array. (It essentially copies the values which are 1 element away from the edge onto the nearest edge).
def zero_deriv_border(p):
p[0, :] = p[1, :] # dp/dx = 0 (left border)
p[:, 0] = p[:, 1] # dp/dy = 0 (bottom border)
p[-1, :] = p[-2, :] # dp/dx = 0 (right border)
p[:, -1] = p[:, -2] # dp/dy = 0 (top border)
Currently, this translates into the following Chapel code (assuming p has zero-based indexing):
proc zeroDerivBorder(p) {
p[0, ..] = p[1, ..]; // dp/dx = 0 (left border)
p[.., 0] = p[.., 1]; // dp/dy = 0 (bottom border)
p[p.domain.dim(0).high, ..] = [p.domain.dim(0).high-1, ..]; // dp/dx = 0 (right border)
p[.., p.domain.dim(1).high] = [.., p.domain.dim(1).high-1]; // dp/dy = 0 (top border)
}
The second two lines are unfortunately long, and indicate to me that it would be nice to have something similar to the python syntax available in chapel arrays.
Here is the same procedure with the proposed change (which also works with non-zero-based indexing):
proc zeroDerivBorder(p) {
p[^+, ..] = p[^+1, ..]; // dp/dx = 0 (left border)
p[.., ^+] = p[.., ^+1]; // dp/dy = 0 (bottom border)
p[^-, ..] = p[^-1, ..]; // dp/dx = 0 (right border)
p[.., ^-] = p[.., ^-1]; // dp/dy = 0 (top border)
}
Invoking ^+
or ^-
Outside of an Indexing Context:
This proposal would require that ranges support the new syntax; however this raises the question of what the new syntax would mean outside the context of array indexing.
For example what would the following code print?
for i in 0..^- {
writeln(i);
}
I think the simplest solution would be to throw an error whenever the new syntax is invoked outside the context of array indexing. I would also be open to other suggestions.