%**The Haskell 98 Library Report: Arrays
%**~header
\section{Arrays}
\label{arrays}
\index{array}
\outline{
\inputHS{lib-hdrs/Array}
}
\Haskell{} provides indexable {\em 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@.
\subsection{Array Construction}
If @a@ is an index type and @b@ is any type, the type of arrays with
indices in @a@ and elements in @b@ is written @Array a b@.\indextycon{Array}
An array may be created by the function @array@\indextt{array}.
The first argument of @array@ is a pair of {\em bounds}, each of the
index type of the array. These bounds are the lowest and
highest indices in the array, in that order. For example, a
one-origin vector of length @10@ has bounds @(1,10)@, and a one-origin @10@
by @10@ matrix has bounds @((1,1),(10,10))@.
The second argument of @array@ is a list of {\em 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.~$\bot$) 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.~$\bot$).
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:
\bprog
@
a = array (1,100) ((1,1) : [(i, i * a!(i-1)) | i <- [2..100]])
@
\eprog
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 (i.e.~$\bot$).
Figure~\ref{array-examples} shows some examples that use the
@array@ constructor.
\begin{figure}[tb]
\begin{outlineenv}\small
@
-- Scaling an array of numbers by a given number:
scale :: (Num a, Ix b) => a -> Array b a -> Array b a
scale x a = array b [(i, a!i * x) | i <- range b]
where b = bounds a
-- Inverting an array that holds a permutation of its indices
invPerm :: (Ix a) => Array a a -> Array a a
invPerm a = array b [(a!i, i) | i <- range b]
where b = bounds a
-- The inner product of two vectors
inner :: (Ix a, Num b) => Array a b -> Array a b -> b
inner v w = if b == bounds w
then sum [v!i * w!i | i <- range b]
else error "inconformable arrays for inner product"
where b = bounds v
@
\end{outlineenv}
\ecaption{Array examples}
\label{array-examples}
\end{figure}
The @(!)@\index{""!@@{\tt {\char'041}}} operator denotes array subscripting.
% array subscripting -- if the index lies outside the bounds of the
% array, the result is undefined.
The @bounds@\indextt{bounds} function
applied to an array returns its bounds.
The functions @indices@\indextt{indices}, @elems@\indextt{elems}, and
@assocs@,\indextt{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@\indextt{listArray}.
If, in any dimension, the lower bound is greater than the upper bound,
then the array is legal, but empty. Indexing an empty array always
gives an array-bounds error, but @bounds@ still yields the bounds
with which the array was constructed.
\subsubsection{Accumulated Arrays}
\index{array!accumulated}
Another array creation function, @accumArray@,\indextt{accumArray}
relaxes the restriction that a given index may appear at most once in
the association list, using an {\em accumulating function} which
combines the values of associations with the same index.
% \cite{nikhil:id-nouveau,wadler:array-primitive}:
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:
\bprog
@
hist :: (Ix a, Num b) => (a,a) -> [a] -> Array a b
hist bnds is = accumArray (+) 0 bnds [(i, 1) | i<-is, inRange bnds i]
@
\eprog
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.
\subsection{Incremental Array Updates}
\label{array-update}
The operator @(//)@\indextt{//} 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@\indextt{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@:\nopagebreak[4]
\bprog
@
accumArray f z b = accum f (array b [(i, z) | i <- range b])
@
\eprogNoSkip
\subsection{Derived Arrays}
\index{array!derived}
The two functions @fmap@\indextt{fmap} and @ixmap@\indextt{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 @fmap@ function transforms the array values while
@ixmap@ allows for transformations on array indices.
Figure~\ref{derived-array-examples} shows some examples.
\begin{figure}[tb]
\begin{outlineenv}\small
@
-- 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'),(_,u')) = bounds x
-- Diagonal of a matrix (assumed to be square)
diag :: (Ix a) => Array (a,a) b -> Array a b
diag x = ixmap (l,u) (\i->(i,i)) x
where
((l,_),(u,_)) = bounds x
-- Projection of first components of an array of pairs
firstArray :: (Ix a) => Array a (b,c) -> Array a b
firstArray = fmap (\(x,y)->x)
@
\end{outlineenv}
\ecaption{Derived array examples}
\label{derived-array-examples}
\end{figure}
\subsection{Library {\tt Array}}
\label {Libarray}
\inputHS{lib-code/Array}
%**~footer