next up previous contents index
Next: Arrays Up: ValuesTypes, and Constants Previous: Attributes

Multi-Element Indexing

 

  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.

Integer Indices

 

    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 gif, 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.)

Boolean Indices

 

      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 gif, 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.

Assigning Multiple Elements

 

  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:12
assigns 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]] := 0
sets those same elements to 0.

The same sorts of operations can be done using masks:

    a[a > 7] := 32
changes 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 gif, 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 gif.

    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.      

Accessing and Modifying Multiple Record Fields

 

  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 gif), 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 gif, 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] := r
so that now r.x is "hello", r.y is (still) T, and r.z is 1.    


next up previous contents index
Next: Arrays Up: ValuesTypes, and Constants Previous: Attributes

Thu Nov 13 16:44:05 EST 1997