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";
}