module Array ( module Ix, -- export all of Ix for convenience Array, array, listArray, (!), bounds, indices, elems, assocs, accumArray, (//), accum, ixmap ) where import Ix infixl 9 !, // data (Ix a) => Array a b = MkArray (a,a) (a -> b) deriving () array :: (Ix a) => (a,a) -> [(a,b)] -> Array a b listArray :: (Ix a) => (a,a) -> [b] -> Array a b (!) :: (Ix a) => Array a b -> a -> b bounds :: (Ix a) => Array a b -> (a,a) indices :: (Ix a) => Array a b -> [a] elems :: (Ix a) => Array a b -> [b] assocs :: (Ix a) => Array a b -> [(a,b)] accumArray :: (Ix a) => (b -> c -> b) -> b -> (a,a) -> [(a,c)] -> Array a b (//) :: (Ix a) => Array a b -> [(a,b)] -> Array a b accum :: (Ix a) => (b -> c -> b) -> Array a b -> [(a,c)] -> Array a b ixmap :: (Ix a, Ix b) => (a,a) -> (a -> b) -> Array b c -> Array a c instance Functor (Array a) where ... instance (Ix a, Eq b) => Eq (Array a b) where ... instance (Ix a, Ord b) => Ord (Array a b) where ... instance (Ix a, Show a, Show b) => Show (Array a b) where ... instance (Ix a, Read a, Read b) => Read (Array a b) where ... |
Haskell provides indexable arrays, which may be thought of as functions whose domains are isomorphic to contiguous subsets of the integers. Functions restricted in this way can be implemented efficiently; in particular, a programmer may reasonably expect rapid access to the components. To ensure the possibility of such an implementation, arrays are treated as data, not as general functions.
Since most array functions involve the class Ix, this module is exported from Array so that modules need not import both Array and Ix.
The second argument of array is a list of associations
of the form (index, value). Typically, this list will
be expressed as a comprehension. An association (i, x) defines the
value of the array at index i to be x. The array is undefined (i.e. _|_) if
any index in the list is out of bounds. If any two associations in the
list have the same index, the value at that index is undefined (i.e. _|_).
Because the indices must be checked for these errors, array is
strict in the bounds argument and in the indices of the association list,
but nonstrict in the values. Thus, recurrences such as the following are
possible:
a = array (1,100) ((1,1) : [(i, i * a!(i-1)) | i <- [2..100]])
Not every index within the bounds of the array need
appear in the association list, but the values associated with indices
that do not appear will be undefined.
Figure 2 shows some examples that use the
array constructor.
The (!) operator denotes array subscripting. The bounds function applied to an array returns its bounds. The functions indices, elems, and assocs, when applied to an array, return lists of the indices, elements, or associations, respectively, in index order. An array may be constructed from a pair of bounds and a list of values in index order using the function listArray.
Another array creation function, accumArray,
relaxes the restriction that a given index may appear at most once in
the association list, using an accumulating function which
combines the values of associations with the same index.
The first argument of accumArray is the accumulating function; the
second is an initial value; the remaining two arguments are a bounds
pair and an association list, as for the array function.
For example, given a list of values of some index type, hist
produces a histogram of the number of occurrences of each index within
a specified range:
hist :: (Ix a, Num b) => (a,a) -> [a] -> Array a b
hist bnds is = accumArray (+) 0 bnds [(i, 1) | i<-is, inRange bnds i]
If the accumulating function is strict, then accumArray is
strict in the values, as well as the indices, in the
association list. Thus, unlike ordinary arrays,
accumulated arrays should not in general be recursive.
The operator (//) takes an array and a list of pairs and returns an array identical to the left argument except that it has been updated by the associations in the right argument. (As with the array function, the indices in the association list must be unique for the updated elements to be defined.) For example, if m is a 1-origin, n by n matrix, then m//[((i,i), 0) | i <- [1..n]] is the same matrix, except with the diagonal zeroed.
accum f takes an array
and an association list and accumulates pairs from the list into
the array with the accumulating function f. Thus accumArray
can be defined using accum:
accumArray f z b = accum f (array b [(i, z) | i <- range b])
The two functions map and ixmap derive new arrays from existing ones; they may be thought of as providing function composition on the left and right, respectively, with the mapping that the original array embodies. The map function transforms the array values while ixmap allows for transformations on array indices. Figure 3 shows some examples.
-- A rectangular subarray subArray :: (Ix a) => (a,a) -> Array a b -> Array a b subArray bnds = ixmap bnds (\i->i) -- A row of a matrix row :: (Ix a, Ix b) => a -> Array (a,b) c -> Array b c row i x = ixmap (l',u') (\j->(i,j)) x where ((l,l'),(u,u')) = bounds x -- Diagonal of a square matrix diag :: (Ix a) => Array (a,a) b -> Array a b diag x = ixmap (l,u) (\i->(i,i)) x where ((l,l'),(u,u')) | l == l' && u == u' = bounds x -- Projection of first components of an array of pairs firstArray :: (Ix a) => Array a (b,c) -> Array a b firstArray = map (\(x,y)->x) Derived array examples |