next up previous contents index
Next: References Up: ValuesTypes, and Constants Previous: Multi-Element Indexing



    Glish also has multi-dimensional arrays. These arrays can be manipulated much like single dimension vectors. There are operations which permit ``slices'' and ``picks'' to be taken of arrays, and most of the functions and operators work on arrays as well as one dimensional vectors.

Creating Arrays


Typically,     an array is created using the array   function. This function takes an arbitrary number of arguments. The first argument is the initial value for the new array. The rest of the arguments are the length of the dimensions of the array. So, to create a cube,

    cube := array(0,4,4,4)
creates a three dimensional array which is initialized to all zeros. Each of the three dimensions of the cube has a length of four. The array can also be initialized with a vector:
    plane := array(1:4,4,3)
This creates a matrix which has two dimensions. Each of the three columns are initialized to [1, 2, 3, 4]. If the initialization vector has fewer elements than the final array, the vector is replicated to fill the array. If the initialization vector has more elements than the final array, only the initial portion of the vector will be used to fill the array; the rest will be ignored. So, in
    one := array([3,5],3,3)
    two := array(1:25,3,4)
one is filled with repeating [3, 5] in column-major order. The first (and third) columns equal [3, 5, 3] while the second equals [5, 3, 5]. In the case of two, the elements of the array are filled (in column-major order) with integers starting with 1 and ending with 12; the first column of two contains [1, 2, 3].      

Indexing Arrays


Arrays   are indexed using the the [] operator. This operator is used with one subscript for each dimension of the array. This operator can be used to access a single element, a contiguous section, or elements sprinkled throughout the array.

Element Indexing with Subscripts


Access to individual elements    of arrays is accomplished much like indexing one dimensional vectors. The only difference is that instead of a single subscript, multiple subscripts are used, one for each dimension. For example,

    x := array([3,5],3,3)
    for (i in 1:3 ) x[i,i] *:= 3
a three by three matrix is assigned to x, in this example. The for loop (§ 5.5.2, page gif) then multiplies each of the elements along the diagonal of x by 3. If x had three dimensions instead of two, then three subscripts would have been required. As with vector indexing, the array subscripting operation can be use to get as well as put values in the array.  

Slice Indexing with Subscripts


Multiple elements of arrays    can be accessed as well. Often, however, the result will be another array instead of a vector. For example,

    cube := array([3,5],3,3,3)
    for (i in 1:3 ) x[1:3,1:3,i] *:= i
here cube is a three dimensional array, and the for loop multiplies each plane in the cube by its position. The first plane is unchanged, i.e. multiplied by 1, but the second plane is multiplied by 2 and the last plane is multiplied by 3. x[1:3,1:3,i] selects a whole plane from the cube, in particular the ith plane. The 1:3 in the first two subscript slots selects all of the elements in those dimensions, but the third subscript, i, limits selection in that direction. Selecting large sections of the array in this manner is often referred to as ``slicing'' the array.

Often, all of the elements along a dimension are not selected. In this case, the elements of interest must be listed explicitly (as was done above). When all of the elements along a certain dimension are desired, as was the case above, omitting a particular subscript implies all of the elements along that dimension. The example above could be rewritten:

    cube := array([3,5],3,3,3)
    for (i in 1:3 ) x[,,i] *:= i
The omitted subscripts imply the whole dimension. Whole array operations are permitted much as they were for vector access (see § 3.6.3).  

Element Indexing with an Array


    The ability to use subscripts to access either single elements or slices of an array covers most of the operations typically performed on arrays. Sometimes, however, this is not sufficient. Suppose, for example, one wanted to access the top left corner and bottom right corner of a matrix. With the available subscript operations presented above, the best that can be done is the following:

    mat := array(1:16,4,4)
    tlc_brc := mat[[1,4],[1,4]]
This unfortunately sets tlc_brc equal to a two by two array which contains each of the four corners. To handle array accesses where desired array elements are distributed throughout the array, a single subscript which is itself an array can be used to access the elements of the array. The problems with the example above could be fixed as follows:
    tlc_brc := mat[array([1,4],2,2)]
In this example, an array is used as the single index into the array. The rows of the subscript array indicate the elements of mat to be selected. So the number of columns of the subscript array will always equal the dimensionality of the array being accessed. In the example above, the first point, [1, 1], is the first row of the subscript array, and the second point, [4, 4], is the second (and final) row. The subscript array can have an arbitrary number of rows. This type of subscripting implements what is commonly known as a ``pick'' operation. Array subscript indexing can be use to both get and put elements of the array.

Arrays which are used as an array index should not be of type boolean See § 3.7.4 for a discussion of boolean array indexes.  

Slice Indexing with a Record


  Along with ``picks'', sometimes specifying slices with multiple subscripts doesn't work either. The situation where this may happen is when writing a function which manipulates an array of any dimensionality. In this case, use of multiple subscripts breaks down. Indexing with a subscript array solves this problem for accessing individual elements of the array, and indexing with a record solves it for accessing slices of the array.

  A record can be used to specify the elements of the slice. The length (see § 9.3, page gif) of the record must equal the dimensionality of the array. The field values of the record are used irrespective of the field label, and each of the field values acts as one of the subscripts in the slice operation. For example,

    cube := array(1:27,3,3,3)
    one := cube[,3,]
    two := cube[[a=1:3,b=3,c=[]]]
here cube is assigned an array with three dimensions. one is assigned the ``left'' slice. The assignment to two demonstrates how one can use a record to index the same slice of the array. Each of the fields of the record correspond to the respective dimension of the array. The position of the field rather than the field name is used to determine which dimension it specifies. In the assignment to two, the first field contains 1:3 so the entire first dimension participates in the slice, the second field to is set to 3 thus limiting the second dimension, and since the final field is an empty array, the entire third dimension participates in the slice. one and two have the same value after these assignment. Since the field names are ignored, numeric subscripts can also be used to set up the index:
    index := [=]
    index[1] := []
    index[2] := 3
    index[3] := 1:3
    three := cube[index]
In the example, index is set up with a record equivalent (for the purposes of indexing arrays) to the one that was used to set two above. Thus after this example, one, two and three all have the same value.

As was indicated above,       this is mainly of use when the shape of the array is not known ahead of time. Functions which deal with arrays of a non-predetermined shape can use the system defined attribute shape which is defined for all arrays to determine the shape of the array at runtime. For arrays, this shape attribute always contains a vector which indicates the shape of the array. In the following,

    func s( ary ) { return ary::shape }
the function s simply returns the shape of arrays. If s is invoked with cube from the previous example as an argument, the result is [3, 3, 3].        

Arrays as Vectors


  As was mentioned earlier, most functions and operators will accept arrays or vectors as parameters. All   of the arithmetic, comparison, and logical operators operate element by element on arrays as well as vectors. This is not matrix arithmetic but rather element by element array arithmetic.

It is easy for users to write functions which operate on both vectors and arrays as well. This is a result of the fact that arrays are implemented as a vector plus a shape attribute. So an array can easily be constructed from a vector, for example

    vec := [3, 4, 9, 12, 8, 2, 10, 21, 5]
    vec::shape := [3, 3]
    v := vec[3, 3]
Here a vector, vec, was constructed, and then simply by adding a shape attribute was turned into an array, and from that point on it can be manipulated as an array. v equals 5. Deleting the shape attribute turns vec back into a vector,
    vec:: := [=]
Now, an attempt to use array addressing, e.g. vec[3, 3], results in an error.

The underlying vector can always be accessed for any array, and the shape attribute need not be cleared. For arrays, whenever a single non-boolean vector value is used as a subscript, the underlying vector is accessed. In the following,

    vec := 1:9
    vec::shape := [3, 3]
    a := vec[3, 3]
    b := vec[len(vec)]
both array accesses are valid, and a and b both have the same value, 9. length applied to an array returns the length of the underlying vector.

If   the underlying vector and the shape of an array, do not match errors can result. If the length of the vector is greater than the length implicit in the shape, then there is no problem, only a portion of the underlying vector will be use. If, however, the vector length is less than the length implied by the shape, runtime errors will be reported when accesses are attempted past the end of the vector. If the array function is used, these errors cannot happen.    

Operators and Boolean Indices


    As was discussed above, many of the operators and functions operate element by element on arrays as well as vectors. This is how boolean indices are used with arrays. In the following,

    ary := array([1, -3, -4, 7, 2, -1, 10, -6, 3],3,3)
    ary[ary < 0] := 0
ary is assigned a two dimensional array. The next statement alters all of the elements of the array which had a negative values by setting them to zero. The comparison operators applied to arrays operate element by element, and the result is another array. If two arrays are involved, their lengths must be equal, and if a scalar and an array are involved, as above, the scalar is compared with each element of the array. A boolean array is generated as a result. This boolean array is then used to select elements from the array in the same way that boolean masks were used with vectors (§ 3.6.2, page gif). A single boolean vector or array subscript acts as a mask, and as a result, the length of the boolean subscript must equal the length of the array being indexed.    

  Arithmetic operations applied to arrays are identical to the corresponding vector operations. If an arithmetic operator is applied to two arrays, their lengths must be equal, and the result is the array generated by the element by element application of the operator. In the following,

    ten := array(1:9,3,3) + array(9:1,3,3)
    ident := array(0,4,4)
    ident[array(1:4,4,2)] := 1
    o := array(seq(1,4,.2),4,4)
    r := ident * o
ten is a two dimensional array, and each of its elements equals 10. ident, after the two assignments, is the identity matrix with 1s along the diagonal and 0s elsewhere. o has 1:4 along its diagonal and the intermediate numbers elsewhere. When ident is multiplied by o the result, r, is a matrix with 1:4 along its diagonal, but zeros elsewhere. This demonstrates the nature of array arithmetic within Glish. Arithmetic operators perform element by element operations; they do not obey the rules of matrix arithmetic.  

  Logical operators also perform element by element operations. For example,

    _2b := array(F,3,3)
    the_question := _2b | ! _2b
here _2b is a two dimensional boolean array. the_question is a two dimensional array all of whose values are T. This is a result of the element by element nature of Glish logical operations.  


next up previous contents index
Next: References Up: ValuesTypes, and Constants Previous: Multi-Element Indexing

Thu Nov 13 16:44:05 EST 1997