While Glish supports the ``usual" form of single-element vector access familiar to C and FORTRAN programmers, it also provides ways for accessing or modifying more than one vector element at a time.
Glish vector indices needn't be scalar values; the indices can also be multi-element vectors. The indices have different meanings depending on whether their type is integer or boolean. We discuss each of these in turn below.
If the index's type is integer (or numeric and hence convertible to integer; but not boolean), then the values of the index indicate the desired elements of the indexed vector. For example, if we have
a := [5, 9, 0, -3, 7, 1] b := [4, 2]then
a[b]yields the vector
[-3, 9]since -3 is the 4th element of a and 9 is the 2nd element. There's no special need for the vector index to be a variable; it could just as soon be a constant:
a[[4,2]](which is equivalent to a[b]) or a vector-valued expression:
a[b+2]yields
[1, -3]since those are the 6th and 4th elements of a.
Since the : operator yields an integer vector, you can use it to access a contiguous sequence of elements in a vector:
a[3:5]yields
[0, -3, 7]since those are the 3rd through 5th elements of a. Similarly,
a[2:1]yields
[9, 5]as those are the 2nd and 1st elements of a. If we have a vector x that we copy into rev_x in reverse order, we could use:
rev_x := x[len(x):1]
The ind function provides a convenient way for generating a vector value's indices:
ind(x)is equivalent to:
1:len(x)
The seq function provides a somewhat more flexible way to generate vector indices. seq takes one, two, or three arguments. For our purposes here we will limit these arguments to be integers; see § 9.3, page , for a complete discussion of seq. If seq is invoked with just one scalar argument then it returns a vector of the integers from 1 to that value:
seq(7)yields
[1, 2, 3, 4, 5, 6, 7]for example. If it is invoked with a single non-scalar argument then it returns a vector of the integers from 1 to the length of the argument:
seq([3, 1, 4, 1, 5, 9])yields
[1, 2, 3, 4, 5, 6]
If seq is invoked with two arguments then it returns the integers between the two, inclusive:
seq(5,2)yields
[5, 4, 3, 2]If the first argument is a non-scalar then its first element is used to determine where the sequence begins.
If invoked with three arguments then seq returns the integers between the first two using the third as a stride. It starts with the first value and works its way to the second, each time incrementing by the stride. It stops when it passes the second argument. So
seq(3,10,2)yields
[3, 5, 7, 9]and
seq(20,8,-4)yields
[20, 16, 12, 8]while
x[seq(1,len(x),2)]yields every other element of x. Note that in the second example, using
seq(20,8,4)would result in a run-time error. If a stride is given then it must reflect the direction in which the sequence will proceed. (This is perhaps a bug.)
A boolean vector index forms a mask that picks out those elements for which the mask is true. For example,
a := "hello there, how are you?" print a[[F,T,T,F,F]]will print ``there, how". Similarly,
y := x[x > 5 & x < 12]will assign to y a vector of just those elements of x that are greater than 5 and less than 12, since the x > 5 & x < 12 operation returns a boolean mask that is true for those elements of x greater than 5 and less than 12, and false for the remainder. Another example:
max(x[x < 10])will return the largest element of x that is less than 10 (see § 9.3, page , for a discussion of max and other related functions).
Often we want to know the indices of those vector elements with a certain property, rather than the values of those elements. The following illustrates the idiom for doing so:
neg_indices := ind(x)[x < 0]Here we have assigned to
neg_indices
the indices of those
elements of x that are less than 0. Thus
x[neg_indices]and
x[x < 0]are equivalent expressions.
Boolean indices must have the same number of elements as the indexed vector, or else a run-time error occurs.
In addition to using vector indices to access multiple vector elements, you can also use them to modify multiple elements.
a[[5,3,7]] := 10:12assigns to the 5th, 3rd, and 7th values of a the numbers 10, 11, and 12, respectively. The right-hand-side of the assignment can also be a scalar value:
a[[5,3,7]] := 0sets those same elements to 0.
The same sorts of operations can be done using masks:
a[a > 7] := 32changes all elements of a that are greater than 7 to be 32, and
x[x < 0] := -x[x < 0]is the same as
x := abs(x)(indeed, this is how abs is implemented; see § 9.3, page , for a discussion of abs and other related functions); it converts the negative elements of x to their absolute value.
As with simple, scalar assignments, the types on both sides of the := operator must be compatible, as discussed in § 3.1.3, page .
The right-hand-side must either be a scalar or have the same number of elements as indicated by the indices or mask used on the left-hand-side. For example,
a[1:3] := [2,4]is illegal.
As with vectors, you can access and modify multiple record fields using multi-element indices. For records the index must be a vector of strings. For example,
a := [foo=1, bar=[3.0, 5.3, 7], bletch="hello there"] b := a[["foo", "bar"]]assigns to b a record whose foo field is the integer value 1 and whose bar field is the double vector [3.0, 5.3, 7.0]. Because of how double-quoted string literals are broken up into vectors (see § 3.3.1, page ), the second statement could also have been written:
b := a["foo bar"]
You can assign multiple record fields in a similar fashion:
a["foo bar"] := [x=[9,1], y=T]will change a's foo field to be the integer vector [9,1], and a's bar field to the boolean value T (true). You can also make this sort of assignment by accessing multiple-field elements on the right-hand-side. For example, the following is equivalent:
r := [x=[9,1], y=T, z="ignore me"] a["foo bar"] := r["x y"]For the assignment to be legal, the right-hand-side must be a record with the same number of fields as the left-hand-side (as in the example above). The field names are ignored but the assignment is done field-by-field, left-to-right.
As discussed in § 3.4.4, page , you can access records using numeric subscripts, and just as can be done with vector values, you can use multiple numeric subscripts to access and modify more than one field in the record. For example, the following reverses the fields of r:
r := [x=1, y=T, z="hello"] r[3:1] := rso that now r.x is "hello", r.y is (still) T, and r.z is 1.