Next: , Previous: , Up: Data structures   [Contents][Index]

### 14.8 Multi-dimensional Arrays

Arrays are heterogeneous data structures that generaize vectors to multiple indexes or dimensions. Instead of a single integer index, there are multiple indexes: An index is a vector of integers; the length of a valid index sequence is the rank or the number of dimensions of an array.

Kawa multi-dimensional arrays follows the by SRFI-25 specification, with additions from Racket’s math.array package and other sources.

An array whose rank is 1, and where the (single) lower bound is 0 is a sequence. Furthermore, if such an array is simple (not created by `share-array`) it will be implemented using a `<vector>`. Uniform vectors and strings are also arrays in Kawa.

A rank-0 array has a single value. It is essentially a box for that value. Functions that require arrays may treat non-arrays as a rank-0 array containing that value.

An array of rank 2 is frequently called a matrix.

Note that Kawa arrays are distinct from Java (native) arrays. The latter is a simpler one-dimensional vector-like data structure, which is used to implement Kawa arrays and vectors.

Procedure: array? obj

Returns `#t` if obj is an array, otherwise returns `#f`.

#### 14.8.1 Array shape

The shape of an array consists of bounds for each index.

The lower bound b and the upper bound e of a dimension are exact integers with `(<= b e)`. A valid index along the dimension is an exact integer i that satisfies both `(<= b i)` and `(< i e)`. The length of the array along the dimension is the difference `(- e b)`. The size of an array is the product of the lengths of its dimensions.

A procedure that requires a shape accepts any of the following:

• A vector of simple ranges, one for each dimension, all of who are bounded (finite), consist of integer values, and have a step of 1. Each range, which is usually written as `[b <: e]`, expresses the bounds of the corresponding dimension. For example `[[0 <: 5] [2 <=: 6]]` is the shape of a rank-2 array, where the first index can be from 0 to 5 (exclusive), while the second index can be from 2 to 6 (inclusive).
• A vector of simple integers. Each integer e is an upper bound, and is equivalent to the range `[0 <: e]`.
• A vector consisting of a mix of integers and ranges.
• A rank-2 array S whose own shape is `[r 2]`. For each dimension k (where `(<= k 0)` and `(< k r)`), the lower bound bk is `(S k 0)`, and the upper bound ek is `(S k 1)`.
Procedure: shape bound ...

Returns a shape. The sequence bound ... must consist of an even number of exact integers that are pairwise not decreasing. Each pair gives the lower and upper bound of a dimension. If the shape is used to specify the dimensions of an array and bound ... is the sequence b0 e0 ... bk ek ... of n pairs of bounds, then a valid index to the array is any sequence j0 ... jk ... of n exact integers where each jk satisfies `(<= bk jk)` and `(< jk ek)`.

The shape of a d-dimensional array is a d * 2 array where the element at k 0 contains the lower bound for an index along dimension k and the element at k 1 contains the corresponding upper bound, where k satisfies `(<= 0 k)` and `(< k d)`.

`(shape @bounds)` is equivalent to: `(array [2 (/ (length bounds) 2)] @bounds)`

Procedure: array-rank array

Returns the number of dimensions of array.

```(array-rank
(make-array (shape 1 2 3 4)))
```

Returns 2.

Procedure: array-start array k

Returns the lower bound (inclusive) for the index along dimension k. This is most commonly 0.

Procedure: array-end array k

Returns the upper bound for the index along dimension k. The bound is exclusive - i.e. the first integer higher than the last legal index.

Procedure: array-size array

Return the total number of elements of array. This is the product of `(- (array-end array k) (array-start array k))` for every valid k.

#### 14.8.2 Array types

Type: array
Type: arrayN
Type: array[etype]
Type: arrayN[etype]

The type `array` matches all array values. The type `arrayN`, where N is an integer matches array of rank N. For example `array2` matches rank-2 array - i.e. matrixes.

You can optionally specify the element type etype. This can be a primitive type. For example a `array2[double]` is a rank-2 array whose elements are `double` values.

#### 14.8.3 Array literals and printing

An array literal starts with `#` followed by its rank, followed by a tag that describes the underlying vector (by default `a`), optionally followed by information about its shape, and finally followed by the cells, organized into dimensions using parentheses.

For example, `#2a((11 12 13) (21 22 23))` is a rank-2 array (a matrix) whose shape is `[2 3]` or equivalently `[[0 <: 2] [0 <: 3]]`. It is pretty-printed as:

```╔#2a:2:3═╗
║11│12│13║
╟──┼──┼──╢
║21│22│23║
╚══╧══╧══╝
```
```array-literal ::= array-literal-header datum
array-literal-header ::= `#` rank vectag array-bound*
array-bound ::= [`@`lower]`:`length | `@`lower
vectag ::= `a` | uniform-tag
```

The vectag specifies the type of the elements of the array.

Following the vectag you can optionally include information about the shape: For each dimension you can optionally specify the lower bounds (after the character `"@"`), followed by the length of the dimension (after the character `":"`). The shape information is required if a lower bound is non-zero, or any length is zero.

The datum contains the elements in a nested-list format: a rank-1 array (i.e. vector) uses a single list, a rank-2 array uses a list-of-listrs, and so on. The elements are in lexicographic order.

A uniform u32 array of rank 2 with index ranges 2..3 and 3..4:

```#2u32@2@3((1 2) (2 3))
```

This syntax follow Common Lisp with Guile extensions. (Note that Guile prints rank-0 arrays with an extra set of parentheses. Kawa follows Common Lisp in not doing so.)

When an array is printed with the `write` function, the result is an `array-literal`. Printing with `display` formats the array in a rectangular grid using the `format-array` procedure. (Note that `format-array` is only used when the output is in column 0, because Kawa has very limited support for printing rectangles.)

Procedure: format-array value [element-format]

Produce a nice “pretty” display for value, which is usually an array.

The top line includes an `array-literal-header`. The lower bound are only printed if non-zero. The dimension lengths are printed if there is zoom, or if one of them is zero.

```#|kawa:34|# (! arr (array [[1 <=: 2] [1 <=: 3]]
#|.....35|#   #2a((1 2) (3 4)) 9 #2a((3 4) (5 6))
#|.....36|#   [42 43] #2a:1:3((8 7 6)) #2a((90 91) (100 101))))
#|kawa:37|# arr
╔#2a@1:2@1:3════╤═════════╗
║#2a═╗  │      9│#2a═╗    ║
║║1│2║  │       │║3│4║    ║
║╟─┼─╢  │       │╟─┼─╢    ║
║║3│4║  │       │║5│6║    ║
║╚═╧═╝  │       │╚═╧═╝    ║
╟───────┼───────┼─────────╢
║╔#1a:2╗│#2a:1:3│╔#2a:2:2╗║
║║42│43║│║8│7│6║│║ 90│ 91║║
║╚══╧══╝│╚═╧═╧═╝│╟───┼───╢║
║       │       │║100│101║║
║       │       │╚═══╧═══╝║
╚═══════╧═══════╧═════════╝
```

If element-format is specified, it is a format string used for format each non-array:

```#|kawa:38|# (format-array arr "~4,2f")
╔#2a@1:2@1:3══╤════════════════╤═══════════════╗
║╔#2a:2:2══╗  │            9.00│╔#2a:2:2══╗    ║
║║1.00│2.00║  │                │║3.00│4.00║    ║
║╟────┼────╢  │                │╟────┼────╢    ║
║║3.00│4.00║  │                │║5.00│6.00║    ║
║╚════╧════╝  │                │╚════╧════╝    ║
╟─────────────┼────────────────┼───────────────╢
║╔#1a:2╤═════╗│╔#2a:1:3══╤════╗│╔#2a:2:2══════╗║
║║42.00│43.00║│║8.00│7.00│6.00║│║ 90.00│ 91.00║║
║╚═════╧═════╝│╚════╧════╧════╝│╟──────┼──────╢║
║             │                │║100.00│101.00║║
║             │                │╚══════╧══════╝║
╚═════════════╧════════════════╧═══════════════╝
```

If the rank is more than 2, then each “layer” is printed separated by double lines.

```#|kawa:42|# (array-reshape [1 <=: 24] [3 2 4])
╔#3a:3:2:4══╗
║ 1│ 2│ 3│ 4║
╟──┼──┼──┼──╢
║ 5│ 6│ 7│ 8║
╠══╪══╪══╪══╣
║ 9│10│11│12║
╟──┼──┼──┼──╢
║13│14│15│16║
╠══╪══╪══╪══╣
║17│18│19│20║
╟──┼──┼──┼──╢
║21│22│23│24║
╚══╧══╧══╧══╝
```

#### 14.8.4 Array construction

See also `array-reshape`

Procedure: array shape obj ...

Returns a new array whose shape is given by shape and the initial contents of the elements are obj ... in row major order. The array does not retain a reference to shape.

Procedure: make-array shape
Procedure: make-array shape value...

Returns a newly allocated array whose shape is given by shape. If value is provided, then each element is initialized to it. If there is more than one value, they are used in order, starting over then the values are exhausted. If there is no value, the initial contents of each element is unspecified. (Actually, it is the `#!null`.) The array does not retain a reference to shape.

```#|kawa:16|# (make-array [2 4] 1 2 3 4 5)
╔#2a:2:4╗
║1│2│3│4║
╟─┼─┼─┼─╢
║5│1│2│3║
╚═╧═╧═╧═╝
```

Compatibility: Guile has an incompatible `make-array` procedure.

Procedure: build-array shape procedure

Construct a “virtual array” of the given shape, which uses no storage for the elements. Instead, elements are calculated on-demand by calling procedure, which takes a single argument, an index vector.

There is no caching or memoization.

```#|kawa:1|# (build-array [[10 <: 12] 3]
#|....:2|#   (lambda (ind)
#|....:3|#     (let ((x (ind 0)) (y (ind 1)))
#|....:4|#       (- x y))))
#2a@10:2:3
║10│ 9│8║
╟──┼──┼─╢
║11│10│9║
╚══╧══╧═╝
```
Procedure: index-array shape

Return a new immutable array of the specified shape where each element is the corresponding row-major index. Same as `(array-reshape [0 <: size] shape)` where size is the `array-size` of the resulting array.

```#|kawa:1|# (index-array [[1 <: 3] [2 <: 6]])
#2a@1:2@2:4
║0│1│2│3║
╟─┼─┼─┼─╢
║4│5│6│7║
╚═╧═╧═╧═╝
```

#### 14.8.5 Array indexing

If you “call” an array as it it were a function, it is equivalent to using `array-index-ref`, which is generalization of `array-ref`. For example, given a rank-2 array arr with integer indexes i and j, the following all get the element of arr at index `[i j]`.

```(arr i j)
(array-index-ref arr i j)
(array-ref arr i j)
(array-ref arr [i j])
```

Using function-call notation or `array-index-ref` (but not plain `array-ref`) you can do generalized APL-style slicing and indirect indexing. See under `array-index-ref` for examples.

Procedure: array-ref array k ...
Procedure: array-ref array index

Returns the contents of the element of array at index k .... The sequence k ... must be a valid index to array. In the second form, index must be either a vector (a 0-based 1-dimensional array) containing k ....

```(array-ref (array [2 3]
'uno 'dos 'tres
'cuatro 'cinco 'seis)
1 0)
```

Returns `cuatro`.

```(let ((a (array (shape 4 7 1 2) 3 1 4)))
(list (array-ref a 4 1)
(array-ref a (vector 5 1))
(array-ref a (array (shape 0 2)
6 1))))
```

Returns `(3 1 4)`.

Procedure: array-index-ref array index ...

Generalized APL-style array indexing, where each index can be either an array or an integer.

If each index is an integer, then the result is the same as `array-ref`.

Otherwise, the result is an immutable array whose rank is the sum of the ranks of each index. An integer is treated as rank-0 array.

If marr is the result of `(array-index-ref arr M1 M2 ...)` then:

```(marr i11 i12 ... i21 i22 ...)
```

is defined as:

```(arr (M1 i11 i12 ...) (M2 i21 i22 ...) ...)
```

Each Mk gets as many indexes as its rank. If Mk is an integer, then it we use it directly without any indexing.

Here are some examples, starting with simple indexing.

```#|kawa:1|# (define arr (array #2a((1 4) (0 4))
#|.....2|#                    10 11 12 13 20 21 22 23 30 31 32 33))
#|kawa:3|# arr
╔#2a@1:3:4══╗
║10│11│12│13║
╟──┼──┼──┼──╢
║20│21│22│23║
╟──┼──┼──┼──╢
║30│31│32│33║
╚══╧══╧══╧══╝
#|kawa:4|# (arr 2 3)
23
```

If one index is a vector and the rest are scalar integers, then the result is a vector:

```#|kawa:5|# (arr 2 [3 1])
#(23 21)
```

You can select a “sub-matrix” when all indexes are vectors:

```#|kawa:6|# (arr [2 1] [3 1 3])
╔#2a:2:3═╗
║23│21│23║
╟──┼──┼──╢
║13│11│13║
╚══╧══╧══╝
```

Using ranges for index vectors selects a rectangular sub-matrix.

```#|kawa:7|# (arr [1 <: 3] [1 <: 4])
╔#2a:2:3═╗
║11│12│13║
╟──┼──┼──╢
║21│22│23║
╚══╧══╧══╝
```

```#|kawa:8|# (arr [2 1] #2a((3 1) (3 2)))
#3a╤══╗
║23│21║
╟──┼──╢
║23│22║
╠══╪══╣
║13│11║
╟──┼──╢
║13│12║
╚══╧══╝
```

The pseudo-range `[<:]` can be used to select all the indexes along a dimension. To select row 2 (1-origin):

```#|kawa:9|# (arr 2 [<:])
#(20 21 22 23)
```

To reverse the order use `[>:]`:

```#|kawa:10|# (arr 2 [>:])
#(23 22 21 20)
```

To select column 3:

```#|kawa:11|# (arr [<:] 3)
#(13 23 33)
```

If you actually want a column matrix (i.e. with shape `[3 1]` you can write can place the index in a single-element vector:

```#|kawa:12|# (arr [<:] )
#2a╗
║13║
╟──╢
║23║
╟──╢
║33║
╚══╝
```

To expand that column to 5 colums you can repeat the column index:

```#|kawa:13|# [3 by: 0 size: 5]
#(3 3 3 3 3)
#|kawa:14|# (arr [<:] [3 by: 0 size: 5])
╔#2a:3:5═╤══╤══╗
║13│13│13│13│13║
╟──┼──┼──┼──┼──╢
║23│23│23│23│23║
╟──┼──┼──┼──┼──╢
║33│33│33│33│33║
╚══╧══╧══╧══╧══╝
```

#### 14.8.6 Modifying arrays

You can use `set!` to modify one or multiple elements. To modify a single element:

```(set! (arr index ...) new-value)
```

is equivalent to

```(array-set! arr index ... new-value)
```

You can set a slice (or all of the elements). In that case:

```(set! (arr index ...) new-array)
```

is equivalent to:

```(array-copy! (array-index-share arr index ...) new-array)
```
Procedure: array-set! array k ... obj
Procedure: array-set! array index obj

Stores obj in the element of array at index k .... Returns the void value. The sequence k ... must be a valid index to array. In the second form, index must be either a vector or a 0-based 1-dimensional array containing k ....

```(let ((a (make-array
(shape 4 5 4 5 4 5))))
(array-set! a 4 4 4 "huuhkaja")
(array-ref a 4 4 4))
```

Returns `"huuhkaja"`.

Compatibility: SRFI-47, Guile and Scheme-48 have `array-set!` with a different argument order.

Procedure: array-copy! dst src

Compatibility: Guile has a `array-copy!` with the reversed argument order.

Procedure: array-fill! array value

Set all the values array to value.

#### 14.8.7 Transformations and views

A view or transform of an array is an array a2 whose elements come from some other array a1, given some transform function T that maps a2 indexes to a1 indexes. Specifically `(array-ref a2 indexes)` is `(array-ref a1 (T indexes))`. Modifying a2 causes a1 to be modified; modifying a1 may modify a2 (depending on the transform function). The shape of a2 is in generally different than that of a1.

Procedure: array-transform array shape transform

This is a general mechanism for creating a view. The result is a new array with the given shape. Accessing this new array is implemented by calling the transform function on the index vector, which must return a new index vector valid for indexing the original array. Here is an example (using the same `arr` as in the `array-index-ref` example):

```#|kawa:1|# (define arr (array #2a((1 4) (0 4))
#|.....2|#                    10 11 12 13 20 21 22 23 30 31 32 33))
#|kawa:14|# (array-transform arr #2a((0 3) (1 3) (0 2))
#|.....15|#   (lambda (ix) (let ((i (ix 0)) (j (ix 1)) (k (ix 2)))
#|.....16|#                  [(+ i 1)
#|.....17|#                   (+ (* 2 (- j 1)) k)])))
#3a:3@1:2:2
║10│11║
╟──┼──╢
║12│13║
╠══╪══╣
║20│21║
╟──┼──╢
║22│23║
╠══╪══╣
║30│31║
╟──┼──╢
║32│33║
╚══╧══╝
```

The `array-transform` is generalization of `share-array`, in that it does not require the transform to be affine. Also note the different calling conversions for the tranform: `array-transform` takes a single argument (a vector of indexes), and returns a single result (a vector of indexes); `share-array` takes one argument for each index, and returns one value for each index. The difference is historical.

Procedure: array-index-share array index ...

This does the same generalized APL-style indexing as `array-index-ref`. However, the resulting array is a modifiable view into argument array.

Procedure: array-reshape array shape

Creates a new array narray of the given shape, such that `(array->vector array)` and `(array->vector narray)` are equivalent. I.e. the i’th element in row-major-order of narray is the i’th element in row-major-order of array. Hence `(array-size narray)` (as specied from the shape) must be equal to `(array-size array)`. The resulting narray is a view such that modifying array also modifies narray and vice versa.

Procedure: share-array array shape proc

Returns a new array of shape shape that shares elements of array through proc. The procedure proc must implement an affine function that returns indices of array when given indices of the array returned by `share-array`. The array does not retain a reference to shape.

```(define i_4
(let* ((i (make-array
(shape 0 4 0 4)
0))
(d (share-array i
(shape 0 4)
(lambda (k)
(values k k)))))
(do ((k 0 (+ k 1)))
((= k 4))
(array-set! d k 1))
i))
```

Note: the affinity requirement for proc means that each value must be a sum of multiples of the arguments passed to proc, plus a constant.

Implementation note: arrays have to maintain an internal index mapping from indices k1 ... kd to a single index into a backing vector; the composition of this mapping and proc can be recognised as `(+ n0 (* n1 k1) ... (* nd kd))` by setting each index in turn to 1 and others to 0, and all to 0 for the constant term; the composition can then be compiled away, together with any complexity that the user introduced in their procedure.

Here is an example where the array is a uniform vector:

```(share-array
(f64vector 1.0 2.0 3.0 4.0 5.0 6.0)
(shape 0 2 0 3)
(lambda (i j) (+ (* 2 i) j)))
⇒  #2f64((1.0 2.0 3.0) (4.0 5.0 6.0))
```
Procedure: array-flatten array
Procedure: array->vector array

Return a vector consisting of the elements of the array in row-major-order.

The result of `array-flatten` is fresh (mutable) copy, not a view. The result of `array->vector` is a view: If array is mutable, then modifying array changes the flattened result and vice versa.

If array is “simple”, `array->vector` returns the original vector. Specifically, if vec is a vector then:

```(eq? vec (array->vector (array-reshape vec shape)))
```

#### 14.8.8 Miscellaneous

Procedure: format-array value [element-format]

Next: , Previous: , Up: Data structures   [Contents][Index]