Subscripts and slicing
String, blob, and list subscripts can either retrieve a single element, or can perform slicing. Map subscripts can refer only to one element, not a slice, since maps are unordered.
The syntax and semantics for subscripting with an index
or a slice match the rules in Python. All string, blob, and list indexing
is zero-based, and slices include the lower bound but exclude the
upper bound. For example, if
a
is a list, then a[2:5]
is
the same as the list [a[2], a[3], a[4]]
. If the lower
bound is omitted, the slice starts at element zero; if the upper bound
is omitted, the slice continues until the last element. For example,
if the last element of a
has index 9, then a[7:]
is
the same as the list [a[7], a[8], a[9]]
. Here is
the syntax:subscriptExpr ::= expr '[' subscript ']'
subscript ::= expr | ( expr? ':' expr? )
String subscripts and slices are character-based,
and result in strings. For example, if s
is a ustring
,
then s[3]
retrieves the third Unicode character when
you count from 0, which is also of type ustring
.
For rstring
values, subscripts and slices are also
character-based, but all characters are 8-bit, so index computations are
always constant time.
Invalid subscripts cause runtime errors. Runtime errors explains what happens
upon errors. A subscript is invalid if it is out-of-bounds for its
collection, unless it is the target of an assignment to a new key
in a map. For example, if list
v
has three elements,
then only indices 0 <= i <= 2
are valid, and v[i]
is
a runtime error for all i>=3
. However, if map m
has
no key a
, then the assignment m["a"] = 1
performs autovivification
(inserts a new key) for a
and maps it to the value
1. SPL supports autovivification for consistency with Python and with
the C++ standard libraries, but restricts it to cases where the assignment
is a pure write, without an earlier read step. For example: mapOfInt["newKey"] = 3; //auto-vivify "newKey"
tupleOfMap.m["newKey"] = 5; //auto-vivify "newKey"
mapOfMap["oldKey"]["newKey"] = 5; //auto-vivify "newKey"
mapOfMap["newKey"]["oldKey"] = 5; //error: must read from "newKey" first
mapOfInt["newKey"] += 2; //error: must read from "newKey" first
mapOfTuple["newKey"].b = 5; //error: must read from "newKey" first
A slice
x[lower:upper]
is valid even
if lower or upper is out of bounds. Here are some examples: void test() {
list<rstring> x = ["a", "b", "c"];
rstring s = x[4]; //runtime error: index out of bounds
mutable list<rstring> y;
y = x[1 : 5]; // ["b", "c"]
y = x[5 : 5]; // [ ]
y = x[5 : 1]; // [ ]
y = x[5 : ]; // [ ]
y = x[0 : 2]; // ["a", "b"]
y = x[ : 2]; // ["a", "b"]
y = x[2 : 0]; // [ ]
}
Tip: If you are not certain whether a subscript
is valid, guard it with a defensive membership test.
For example:rstring munchkinLand(map<rstring, rstring> places) {
if ("Oz" in places)
return places["Oz"];
return "not found";
}