For Development HEAD DRAFTSearch (procedure/syntax/module):

9.1 gauche.array - Arrays

Module: gauche.array

This module provides multi-dimensional array data type and operations. The primitive API follows SRFI-25. Besides a generic SRFI-25 array that can store any Scheme objects, this module also provides array classes that stores numeric objects efficiently, backed up by homogeneous numeric vectors (see Uniform vectors). An external representation of arrays, using SRFI-10 mechanism, is also provided.

Each element of an N-dimensional array can be accessed by N integer indices, [ i_0 i_1i_N-1 ]. An array has associated shape that knows lower-bound s_k and upper-bound e_k of index of each dimension, where s_k <= e_k, and the index i_k must satisfy s_k <= i_k < e_k. (Note: it is allowed to have s_k == e_k, but such array can’t store any data. It is also allowed to have zero-dimensional array, that can store a single data.). The shape itself is a [ D x 2 ] array, where D is the dimension of the array which the shape represents.

You can pass index(es) to array access primitives in a few ways; each index can be passed as individual argument, or can be ’packed’ in a vector or one-dimensional array. In the latter case, such a vector or an array is called an "index object". Using a vector is efficient in Gauche when you iterate over the elements by changing the vector elements, for it won’t involve memory allocation.

Arrays can be compared by the equal? procedure. Equal? returns #t if two arrays have the same shape and their corresponding elements are the same in the sense of equal?.

Internally, an array consists of a backing storage and a mapping procedure. A backing storage is an object of aggregate type that can be accessed by an integer index. A mapping procedure takes multi-dimensional indices (or index object) and returns a scalar index into the backing storage.

Class: <array-base>

{gauche.array} An abstract base class of array types, that implements generic operations on the array. To create an array instance, you should use one of the following concrete array classes.

Class: <array>
Class: <u8array>
Class: <s8array>
Class: <u16array>
Class: <s16array>
Class: <u32array>
Class: <s32array>
Class: <u64array>
Class: <s64array>
Class: <f16array>
Class: <f32array>
Class: <f64array>

{gauche.array} Concrete array classes. The <array> class implements SRFI-25 compatible array, i.e. an array that can store any Scheme objects. The <u8array> class through <f64array> classes uses a <u8vector> through <f64vector> as a backing storage, and can only store a limited range of integers or inexact real numbers, but they are space efficient.

Reader Syntax: #,(<array> shape obj …)

An array is written out in this format. (Substitute <array> for <u8array> if the array is <u8array>, etc.) shape is a list of even number of integers, and each 2n-th integer and 2n+1-th integer specifies the inclusive lower-bound and exclusive upper-bound of n-th dimension, respectively. The following obj … are the values in the array listed in row-major order.

When read back, this syntax is read as an array with the same shape and content, so it is equal? to the original array.

; an array such that:
;   8 3 4
;   1 5 9
;   6 7 2
#,(<array> (0 3 0 3) 8 3 4 1 5 9 6 7 2)

; a 4x4 identity matrix
#,(<array> (0 4 0 4) 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1)
Function: array? obj

[SRFI-25]{gauche.array} Returns #t if obj is an array, #f otherwise. It is equivalent to (is-a? obj <array-base>).

Function: make-array shape :optional init

[SRFI-25]{gauche.array} Creates an array of shape shape. Shape must be a [ D x 2 ] array, and for each k (0 <= k < D), the [ k 0 ] element must be less than or equal to the [ k 1] element. If init is given, all the elements are initialized by it. Otherwise, the initial value of the elements are undefined.

(make-array (shape 0 2 0 2 0 2) 5)
 ⇒ #,(<array> (0 2 0 2 0 2) 5 5 5 5 5 5 5 5)
Function: make-u8array shape :optional init
Function: make-s8array shape :optional init

Function: make-f32array shape :optional init
Function: make-f64array shape :optional init

{gauche.array} Like make-array, but creates and returns an uniform numeric array.

Function: array-copy array

{gauche.array} Returns a copy of array, with the same class, shape and content.

Function: shape bound …

[SRFI-25]{gauche.array} Takes even number of exact integer arguments, and returns a two-dimensional array that is suitable for representing the shape of an array.

(shape 0 2 1 3 3 5)
 ⇒ #,(<array> (0 3 0 2) 0 2 1 3 3 5)

(shape)
 ⇒ #,(<array> (0 0 0 2))
Function: array shape init …

[SRFI-25]{gauche.array} Creates an array of shape shape, initializing its elements by init ….

(array (shape 0 2 1 3) 'a 'b 'c 'd)
 ⇒ #,(<array> (0 2 1 3) a b c d)
Function: u8array shape init …
Function: s8array shape init …

Function: f32array shape init …
Function: f64array shape init …

{gauche.array} Like array, but creates and returns an uniform numeric array initialized by init ….

(u8array (shape 0 2 0 2) 1 2 3 4)
 ⇒ #,(<u8array> (0 2 0 2) 1 2 3 4)
Function: array-rank array

[SRFI-25]{gauche.array} Returns the number of dimensions of an array array.

(array-rank (make-array (shape 0 2 0 2 0 2))) ⇒ 3
(array-rank (make-array (shape))) ⇒ 0
Function: array-shape array

{gauche.array} Returns a shape array of array.

Function: array-start array dim
Function: array-end array dim
Function: array-length array dim

[SRFI-25+]{gauche.array} Array-start returns the inclusive lower bound of index of dim-th dimension of an array array. Array-end returns the exclusive upper bound. And array-length returns the difference between two. Array-start and array-end are defined in SRFI-25.

(define a (make-array (shape 1 5 0 2)))

(array-start a 0)  ⇒ 1
(array-end a 0)    ⇒ 5
(array-length a 0) ⇒ 4
(array-start a 1)  ⇒ 0
(array-end a 1)    ⇒ 2
(array-length a 1) ⇒ 2
Function: array-size array

{gauche.array} Returns the total number of elements in the array array.

(array-size (make-array (shape 5 9 1 3))) ⇒ 8
(array-size (make-array (shape))) ⇒ 1
(array-size (make-array (shape 0 0 0 2))) ⇒ 0
Function: array-ref array k …
Function: array-ref array index

[SRFI-25]{gauche.array} Gets the element of array array. In the first form, the element is specified by indices k …. In the second form, the element is specified by an index object index, which must be a vector or an one-dimensional array.

Function: array-set! array k … value
Function: array-set! array index value

[SRFI-25]{gauche.array} Sets the element of array array to value. In the first form, the element is specified by indices k …. In the second form, the element is specified by an index object index, which must be a vector or an one-dimensional array.

Function: share-array array shape proc

[SRFI-25]{gauche.array} Creates and returns a new array of shape shape, that shares the backing storage with the given array array. The procedure proc maps the indices of the new array to the indices to the original array, i.e. proc must be a n-ary procedure that returns m values, where n is the dimension of the new array and m is the one of the original array. Furthermore, proc must be an affine function; each mapping has to be a linear combination of input arguments plus optional constant. (Share-array optimizes the mapping function based on the affinity assumption, so proc won’t be called every time the new array is accessed).

Function: array-for-each-index array proc :optional index

{gauche.array} Calls proc with every index of array. If no index argument is provided, proc is called as (proc i j k …), in which (i,j,k,…) walks over the index. It begins from the least index value of each dimension, and latter dimension is incremented faster.

gosh> (define a (array (shape 0 2 0 2) 1 2 3 4))
a
gosh> a
#,(<array> (0 2 0 2) 1 2 3 4)
gosh> (array-for-each-index a (^(i j) (print i","j)))
0,0
0,1
1,0
1,1

This form of passing indexes is simple but not very efficient, though. For better performance, you can pass an index object to an optional argument index, which is modified for each index and passed to proc. The index object must be mutable, and either a vector, an one-dimensional array, an s8vector, an s16vector or an s32vector. The length of the index object must match the rank of the array. Using index object is efficient since the loop won’t allocate. Don’t forget that the index object is destructively modified within the loop.

gosh> (array-for-each-index a (cut format #t "~s\n" <>) (vector 0 0))
#(0 0)
#(0 1)
#(1 0)
#(1 1)

gosh> (array-for-each-index a (cut format #t "~s\n" <>) (s8vector 0 0))
#s8(0 0)
#s8(0 1)
#s8(1 0)
#s8(1 1)

The procedure returns an unspecified value.

Function: shape-for-each shape proc :optional index

{gauche.array} Calls proc with all possible indexes represented by the shape shape. The optional index argument works the same way as array-for-each-index. Returns an unspecified value.

gosh> (shape-for-each (shape 0 2 0 2) (^(i j) (print i","j)))
0,0
0,1
1,0
1,1
Function: tabulate-array shape proc :optional index

{gauche.array} Calls proc over each index represented by the shape shape, and creates an array from the result of proc. The optional index object can be used in the same way as array-for-each-index. The following example creates an identity matrix of the given shape:

(tabulate-array (shape 0 3 0 3) (^(i j) (if (= i j) 1 0)))
  ⇒ #,(<array> (0 3 0 3) 1 0 0 0 1 0 0 0 1)
Function: array-retabulate! array proc :optional index
Function: array-retabulate! array shape proc :optional index

{gauche.array} Calls proc over each index of the given array, and modifies the array’s element by the returned value of proc. The optional index object can be used in the same way as array-for-each-index. The second form takes a shape; it must match the array’s shape. It is redundant, but may allow some optimization in future in case shape is a literal. Returns an unspecified value.

Function: array-map proc array0 array1 …
Function: array-map shape proc array0 array1 …

{gauche.array} The arguments array0, array1, … must be arrays with the same shape. For each set of corresponding elements of the input arrays, proc is called, and a new array of the same shape is created by the returned values. The second form takes a shape argument, which must match the shape of input array(s). It is redundant, but may allow some optimization in future in case shape is a literal.

(array-map - (array (shape 0 2 0 2) 1 2 3 4))
  ⇒ #,(<array> (0 2 0 2) -1 -2 -3 -4)
Function: array-map! array proc array0 array1 …
Function: array-map! array shape proc array0 array1 …

{gauche.array} Like array-map, but the results of proc are stored by the given array, whose shape must match the shape of input array(s). Returns unspecified value.

Function: array->vector array
Function: array->list array

{gauche.array} Returns a fresh vector or a fresh list of all elements in array.

(array->vector
 (tabulate-array (shape 1 3 1 4)
                 (^(i j) (+ (* 10 i) j))))
 ⇒ #(11 12 13 21 22 23)
Function: array-concatenate a b :optional dimension

{gauche.array} Concatenates arrays at the specified dimension. The sizes of the specified dimension of two arrays must match, although the shapes can be different. Arrays can be of any ranks, but two ranks must match.

;;  [a b]              [a b]
;;  [c d] (+)       => [c d]
;;            [e f]    [e f]
(array-concatenate
 (array (shape 0 2 0 2) 'a 'b 'c 'd)
 (array (shape 0 1 0 2) 'e 'f))
 ⇒ #,(<array> (0 3 0 2) a b c d e f)

;;  [a b]     [e]    [a b e]
;;  [c d] (+) [f] => [c d f]
(array-concatenate
 (array (shape 0 2 0 2) 'a 'b 'c 'd)
 (array (shape 0 2 0 1) 'e 'f)
 1)
 ⇒ #,(<array> (0 2 0 3) a b e c d f)

;; The index range can differ, as far as the sizes match
(array-concatenate
 (array (shape 0 2 0 2) 'a 'b 'c 'd)
 (array (shape 1 3 0 1) 'e 'f) 1)
 ⇒ #,(<array> (0 2 0 3) a b e c d f)
Function: array-transpose array :optional dim1 dim2

{gauche.array} The given array must have a rank greater than or equal to 2. Transpose the array’s dim1-th dimension and dim2-th dimension. The default is 0 and 1.

Function: array-rotate-90 array :optional dim1 dim2

{gauche.array} The given array must have a rank greater than or equal to 2. We regard the array as a matrix with dim1-th dimension as rows and dim2-th dimension as columns, and returns a fresh array whose content is filled by rotating array 90 degree clockwise. The defaults of dim1 and dim2 are 0 and 1, respectively.

;; [1 2 3]      [4 1]
;; [4 5 6]  =>  [5 2]
;;              [6 3]
(array-rotate-90 (array (shape 0 2 0 3) 1 2 3 4 5 6))
 ⇒ #,(<array> (0 3 0 2) 4 1 5 2 6 3)

If array has a rank greater than 2, the array is treated as a matrix of subarrays.

Function: array-flip array :optional dimension
Function: array-flip! array :optional dimension

{gauche.array} Flips the content of the array across the dimension-th dimension. (default is 0). array-flip! modifies the content of array and return it. array-flip doesn’t modify array but creates a fresh array with the flipped content and returns it.

;; [1 2 3]  =>  [4 5 6]
;; [4 5 6]      [1 2 3]
(array-flip (array (shape 0 2 0 3) 1 2 3 4 5 6))
 ⇒ #,(<array> (0 2 0 3) 4 5 6 1 2 3)

;; [1 2 3]  =>  [3 2 1]
;; [4 5 6]      [6 5 4]
(array-flip (array (shape 0 2 0 3) 1 2 3 4 5 6) 1)
 ⇒ #,(<array> (0 2 0 3) 3 2 1 6 5 4)
Function: identity-array dimension :optional class

{gauche.array} Returns a fresh identity array of rank 2, with the given dimension. You can pass one of array classes to class to make the result the instance of the class; the default class is <array>.

(identity-array 3)
 ⇒ #,(<array> (0 3 0 3) 1 0 0 0 1 0 0 0 1)

(identity-array 3 <f32array>)
 ⇒ #,(<f32array> (0 3 0 3) 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0)
Function: array-inverse array

{gauche.array} Regards the array as a matrix, and returns its inverse matrix; array must be 2-dimensional, and must have square shape. If array doesn’t satisfy these conditions, an error is thrown.

If array isn’t a regular matrix, #f is returned.

Function: determinant array
Function: determinant! array

{gauche.array} Regards the array as a matrix, and calculates its determinant; array must be 2-dimensional, and must have square shape. If array doesn’t satisfy these conditions, an error is thrown.

determinant! destructively modifies the given array during calculation. It is faster than determinant, which copies array before calculation to preserve it.

Function: array-mul a b

{gauche.array} Arrays a and b must be rank 2. Regarding them as matrices, multiply them together. The number of rows of a and the number of columns of b must match.

;;           [6 5]
;; [1 2 3] x [4 3] => [20 14]
;; [4 5 6]   [2 1]    [56 41]

(array-mul (array (shape 0 2 0 3) 1 2 3 4 5 6)
           (array (shape 0 3 0 2) 6 5 4 3 2 1))
 ⇒ #,(<array> (0 2 0 2) 20 14 56 41)
Function: array-expt array pow

{gauche.array} Raises array to the power of pow; array must be a square matrix, and pow must be a nonnegative exact integer.

Function: array-div-left a b
Function: array-div-right a b

{gauche.array} Inverse of array-mul; array-div-left returns a matrix M such that (array-mul B M) equals to A, and array-div-right returns a matrix M such that (array-mul M B) equals to A. A and B must be a 2-dimensional square matrix. If B isn’t regular, an error is thrown.

Function: array-add-elements array array-or-scalar …
Function: array-add-elements! array array-or-scalar …
Function: array-sub-elements array array-or-scalar …
Function: array-sub-elements! array array-or-scalar …
Function: array-mul-elements array array-or-scalar …
Function: array-mul-elements! array array-or-scalar …
Function: array-div-elements array array-or-scalar …
Function: array-div-elements! array array-or-scalar …

{gauche.array} Element-wise arithmetics. The second argument and after must be an array of the same shape of the first argument, or a number; if it is a number, it is interpreted as an array of the same shape of the first argument, and each element of which is the given number.

Returns an array of the same shape of the first argument, where each element is the result of addition, subtraction, multiplication or division of the corresponding elements of the arguments.

The linear-update version (procedures whose name ends with !) may reuse the storage of the first array to calculate the result. The first array must be mutable. The caller must still use the returned value instead of counting on the side effects.

(array-add-elements (array (shape 0 2 0 2) 1 2 3 4)
                    (array (shape 0 2 0 2) 5 6 7 8)
                    10)
 ⇒ #,(<array> (0 2 0 2) 16 18 20 22)

(array-div-elements (array (shape 0 2 0 2) 1 3 5 7)
                    100
                    (array (shape 0 2 0 2) 2 4 6 8))
 ⇒ #,(<array> (0 2 0 2) 1/200 3/400 1/120 7/800)

If only one argument is passed, these procedures returns the argument itself.

You can mix different types of arrays as long as their shapes are the same. The result is the same type as the first argument.

(array-mul-elements (make-u8array (shape 0 2 0 2) 3)
                    (array (shape 0 2 0 2) 1 3 5 7))
 ⇒ #,(<u8array> (0 2 0 2) 3 9 15 21)
Function: array-negate-elements array
Function: array-negate-elements! array

Returns an array with the same type of the shape of array, but each element is a negation of the corresponding elements in the original array.

(array-negate-elements (array (shape 0 2 0 2) 1 2 3 4))
  ⇒ #,(<array> (0 2 0 2) -1 -2 -3 -4)
Function: array-reciprocate-elements array
Function: array-reciprocate-elements! array

Returns an array with the same type of the shape of array, but each element is a reciprocal of the corresponding elements in the original array.

(array-reciprocate-elements (array (shape 0 2 0 2) 1 2 3 4))
  ⇒ #,(<array> (0 2 0 2) 1 1/2 1/3 1/4)


For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT