532 lines
17 KiB
HTML
532 lines
17 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
|
<html>
|
|
<head>
|
|
<title>SRFI 25: Multi-dimensional Array Primitives</title>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<H1>Title</H1>
|
|
|
|
SRFI 25: Multi-dimensional Array Primitives
|
|
|
|
<H1>Author</H1>
|
|
|
|
Jussi Piitulainen
|
|
|
|
<H1>Status</H1>
|
|
|
|
This SRFI is currently in ``final'' status. To see an explanation of
|
|
each status that a SRFI can hold, see <a
|
|
href="http://srfi.schemers.org/srfi-process.html">here</a>. You can access
|
|
the discussion via
|
|
<a href="http://srfi.schemers.org/srfi-25/mail-archive/maillist.html">
|
|
the archive of the mailing list</a>.
|
|
<P>
|
|
<UL>
|
|
<LI>Draft: 2001/11/12-2002/01/11 </LI>
|
|
<LI>Revised: 2002/02/03</LI>
|
|
<LI>Final: 2002/05/21</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H1>Abstract</H1>
|
|
|
|
<p>
|
|
A core set of procedures for creating and manipulating heterogeneous
|
|
multidimensional arrays is proposed. The design is consistent with the
|
|
rest of Scheme and independent of other container data types. It
|
|
provides easy sharing of parts of an array as other arrays without
|
|
copying, encouraging a declarative style of programming.
|
|
</p>
|
|
|
|
<p>
|
|
The specification is based on an original contribution by Alan Bawden
|
|
in 1993.
|
|
</p>
|
|
|
|
<H1>Rationale</H1>
|
|
|
|
<p>
|
|
The proposed arrays encourage a natural declarative programming
|
|
style. They allow sharing of most any rectangular part of an array
|
|
through an affine index mapping, without copying. But imperative style
|
|
is equally natural.
|
|
</p>
|
|
|
|
<p>
|
|
The design is consistent with the two indexed data structures of
|
|
Scheme: vectors and strings. The design makes arrays a self-contained
|
|
type. These statements are illustrated in the following paragraphs.
|
|
</p>
|
|
|
|
<p>
|
|
First, in the one-dimensional case, the arguments of the following
|
|
relevant calls match exactly.
|
|
</p>
|
|
|
|
<pre>
|
|
(vector-set! v k o)
|
|
(string-set! s k c)
|
|
(array-set! a k o)
|
|
</pre>
|
|
|
|
<p>
|
|
Likewise, <code>make-array</code> matches <code>make-vector</code> and
|
|
<code>make-string</code>. An analogue to <code>vector</code>,
|
|
<code>string</code> and <code>list</code> is provided, alleviating the
|
|
lack of an external representation. Index bounds are specified as for
|
|
<code>substring</code>, lower bound included and upper bound excluded.
|
|
</p>
|
|
|
|
<p>
|
|
Array shapes are specified as arrays. These can be made with a special
|
|
procedure <code>shape</code> that does not have a shape argument. An
|
|
array does not retain a dependence to the shape array. For example,
|
|
mutation of a shape array is allowed.
|
|
</p>
|
|
|
|
<p>
|
|
Index mappings return multiple values as multiple values.
|
|
</p>
|
|
|
|
<p>
|
|
Array dimensions can begin at any index. In particular, the choice
|
|
between <code>0</code> and <code>1</code> is left to the user.
|
|
(Shapes and index objects are zero based, though.)
|
|
</p>
|
|
|
|
<p>
|
|
The ability to pack an index sequence in a vector is useful for
|
|
implementing higher level operations. (The ability to pack it in a
|
|
one-dimensional array lets one use, say, a row of a matrix as an
|
|
index.)
|
|
</p>
|
|
|
|
<p>
|
|
It is not required that vectors not be arrays. It is not required that
|
|
they be, either.
|
|
</p>
|
|
|
|
<H1>Specification</H1>
|
|
|
|
<p>
|
|
Arrays are heterogeneous data structures whose elements are indexed by
|
|
integer sequences of fixed length. The length of a valid index
|
|
sequence is the <dfn>rank</dfn> or the number of <dfn>dimensions</dfn>
|
|
of an array. The <dfn>shape</dfn> of an array consists of bounds for
|
|
each index.
|
|
</p>
|
|
|
|
<p>
|
|
The lower bound <var>b</var> and the upper bound <var>e</var> of a
|
|
dimension are exact integers with
|
|
<code>(<= <var>b</var> <var>e</var>)</code>. A valid
|
|
index along the dimension is an exact integer <var>k</var> that
|
|
satisfies both
|
|
<code>(<= <var>b</var> <var>k</var>)</code> and
|
|
<code>(< <var>k</var> <var>e</var>)</code>. The length of
|
|
the array along the dimension is the difference
|
|
<code>(- <var>e</var> <var>b</var>)</code>. The
|
|
<dfn>size</dfn> of an array is the product of the lengths of its
|
|
dimensions.
|
|
</p>
|
|
|
|
<p>
|
|
A shape is specified as an even number of exact integers. These are
|
|
alternately the lower and upper bounds for the dimensions of an array.
|
|
</p>
|
|
|
|
<p>
|
|
The following ten procedures should be implemented.
|
|
</p>
|
|
|
|
<p><a name="array-p"></a>
|
|
<code>(array? <var>obj</var>)</code><br>
|
|
|
|
Returns <samp>#t</samp> if <var>obj</var> is an array, otherwise
|
|
returns <samp>#f</samp>.
|
|
</p>
|
|
|
|
<p>
|
|
Note: there is no reasonable way to implement this procedure
|
|
accurately in R5RS; <a href="http://srfi.schemers.org/srfi-9/">SRFI
|
|
9</a> (Defining Record Types) specifies a way, and many Scheme
|
|
implementations provide something similar.
|
|
</p>
|
|
|
|
<p><a name="make-array"></a>
|
|
<code>(make-array <var>shape</var>)</code><br>
|
|
<code>(make-array <var>shape</var> <var>obj</var>)</code><br>
|
|
|
|
Returns a newly allocated array whose shape is given by
|
|
<var>shape</var>. If <var>obj</var> is provided, then each element is
|
|
initialized to it. Otherwise the initial contents of each element is
|
|
unspecified. The array does not retain a dependence to
|
|
<var>shape</var>.
|
|
</p>
|
|
|
|
<p><a name="shape"></a>
|
|
<code>(shape <var>bound ...</var>)</code><br>
|
|
|
|
Returns a shape. The sequence <var>bound ...</var> 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 <var>bound ...</var> is
|
|
the sequence <var>b0 e0 ... bk ek ...</var> of <var>n</var> pairs of
|
|
bounds, then a valid index to the array is any sequence <var>j0 ... jk
|
|
...</var> of <var>n</var> exact integers where each <var>jk</var>
|
|
satisfies <code>(<= <var>bk</var> <var>jk</var>)</code>
|
|
and <code>(< <var>jk</var> <var>ek</var>)</code>.
|
|
</p>
|
|
|
|
<p>
|
|
The shape of a <var>d</var>-dimensional array is a
|
|
<var>d</var> × <var>2</var> array where the element at
|
|
<var>k 0</var> contains the lower bound for an index along dimension
|
|
<var>k</var> and the element at <var>k 1</var> contains the
|
|
corresponding upper bound, where <var>k</var> satisfies
|
|
<code>(<= 0 <var>k</var>)</code> and
|
|
<code>(< <var>k</var> <var>d</var>)</code>.
|
|
</p>
|
|
|
|
<p><a name="array"></a>
|
|
<code>(array <var>shape</var> <var>obj ...</var>)</code><br>
|
|
|
|
Returns a new array whose shape is given by <var>shape</var> and the
|
|
initial contents of the elements are <var>obj ...</var> in row major
|
|
order. The array does not retain a dependence to <var>shape</var>.
|
|
</p>
|
|
|
|
<p><a name="array-rank"></a>
|
|
<code>(array-rank <var>array</var>)</code><br>
|
|
|
|
Returns the number of dimensions of <var>array</var>.
|
|
<pre>
|
|
(array-rank
|
|
(make-array (shape 1 2 3 4)))
|
|
</pre>
|
|
|
|
<p>
|
|
Returns <samp>2</samp>.
|
|
</p>
|
|
|
|
<p><a name="array-start"></a>
|
|
<code>(array-start <var>array</var> <var>k</var>)</code><br>
|
|
|
|
Returns the lower bound for the index along dimension <var>k</var>.
|
|
</p>
|
|
|
|
<p><a name="array-end"></a>
|
|
<code>(array-end <var>array</var> <var>k</var>)</code><br>
|
|
|
|
Returns the upper bound for the index along dimension <var>k</var>.
|
|
</p>
|
|
|
|
<!--
|
|
<p>
|
|
<code>(array-shape <var>array</var>)</code><br>
|
|
|
|
Returns a newly allocated shape of <var>array</var>.
|
|
</p>
|
|
|
|
<pre>
|
|
(= (array-rank a)
|
|
(array-ref (array-shape (array-shape a)) 0 1))
|
|
</pre>
|
|
|
|
<p>
|
|
Returns <samp>#t</samp>.
|
|
</p>
|
|
|
|
<p>
|
|
Implementation note: <code>array-shape</code> has <code>(shape 0 2 0
|
|
2)</code> as a fixed point which is reached in two or three iterations
|
|
from any array.
|
|
</p>
|
|
-->
|
|
|
|
<p><a name="array-ref"></a>
|
|
<code>(array-ref <var>array</var> <var>k ...</var>)</code><br>
|
|
<code>(array-ref <var>array</var> <var>index</var>)</code><br>
|
|
|
|
Returns the contents of the element of <var>array</var> at index
|
|
<var>k ...</var>. The sequence <var>k ...</var> must be a valid index
|
|
to <var>array</var>. In the second form, <var>index</var> must be
|
|
either a vector or a 0-based 1-dimensional array containing <var>k
|
|
...</var>.
|
|
</p>
|
|
|
|
<pre>
|
|
(array-ref (array (shape 0 2 0 3)
|
|
'uno 'dos 'tres
|
|
'cuatro 'cinco 'seis)
|
|
1 0)
|
|
</pre>
|
|
|
|
<p>
|
|
Returns <samp>cuatro</samp>.
|
|
</p>
|
|
|
|
<pre>
|
|
(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))))
|
|
</pre>
|
|
|
|
<p>
|
|
Returns <samp>(3 1 4)</samp>.
|
|
</p>
|
|
|
|
<p><a name="array-set!"></a>
|
|
<code>(array-set! <var>array</var> <var>k ...</var> <var>obj</var>)</code><br>
|
|
<code>(array-set! <var>array</var> <var>index</var> <var>obj</var>)</code><br>
|
|
|
|
Stores <var>obj</var> in the element of <var>array</var> at index
|
|
<var>k ...</var>. Returns an unspecified value. The sequence <var>k
|
|
...</var> must be a valid index to <var>array</var>. In the second
|
|
form, <var>index</var> must be either a vector or a 0-based
|
|
1-dimensional array containing <var>k ...</var>.
|
|
|
|
<pre>
|
|
(let ((a (make-array
|
|
(shape 4 5 4 5 4 5))))
|
|
(array-set! a 4 4 4 'huuhkaja)
|
|
(array-ref a 4 4 4))
|
|
</pre>
|
|
|
|
<p>
|
|
Returns <samp>huuhkaja</samp>.
|
|
</p>
|
|
|
|
<p><a name="share-array"></a>
|
|
<code>(share-array <var>array</var> <var>shape</var> <var>proc</var>)</code>
|
|
<br>
|
|
|
|
Returns a new array of shape <var>shape</var> that shares elements of
|
|
<var>array</var> through <var>proc</var>. The procedure
|
|
<var>proc</var> must implement an affine function that returns indices
|
|
of <var>array</var> when given indices of the array returned by
|
|
<code>share-array</code>. The array does not retain a dependence to
|
|
<var>shape</var>.
|
|
</p>
|
|
|
|
<pre>
|
|
(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))
|
|
</pre>
|
|
|
|
<p>
|
|
Note: the affinity requirement for <var>proc</var> means that each
|
|
value must be a sum of multiples of the arguments passed to
|
|
<var>proc</var>, plus a constant.
|
|
</p>
|
|
|
|
<p>
|
|
Implementation note: arrays have to maintain an internal index mapping
|
|
from indices <var>k1 ... kd</var> to a single index into a backing
|
|
vector; the composition of this mapping and <var>proc</var> can be
|
|
recognised as <code>(+ n0 (* n1 <var>k1</var>) ... (* nd
|
|
<var>kd</var>))</code> by setting each index in turn to <code>1</code>
|
|
and others to <code>0</code>, and all to <code>0</code> for the
|
|
constant term; the composition can then be compiled away, together
|
|
with any complexity that the user introduced in their procedure.
|
|
</p>
|
|
|
|
<p>
|
|
This document does not specify any external representation for arrays.
|
|
This document does not specify when arrays are <code>equal?</code>.
|
|
(Indeed, R5RS <code>equal?</code> will do the wrong thing.)
|
|
</p>
|
|
|
|
<h1>Examples</h1>
|
|
|
|
<p>
|
|
The reference implementation comes with a number of files that
|
|
illustrate some ways to use the proposed system (and are very useful
|
|
in <em>testing</em> an implementation; that is their origin).
|
|
</p>
|
|
|
|
<ol>
|
|
|
|
<li> A library <a href="http://srfi.schemers.org/srfi-25/arlib.scm">arlib.scm</a> that contains, among
|
|
several other things, <code>tabulate-array</code> for a more
|
|
useful initialization of a new array, an
|
|
<code>array-equal?</code>, and a <code>transpose</code> that can
|
|
permute the dimensions of an array any which way.
|
|
|
|
<li> A test suite <a href="http://srfi.schemers.org/srfi-25/test.scm">test.scm</a> for <em>array</em>,
|
|
and another test suite <a href="http://srfi.schemers.org/srfi-25/list.scm">list.scm</a> for
|
|
<em>arlib</em>.
|
|
|
|
<li> A rudimentary display procedure <code>(play array)</code> in <a
|
|
href="http://srfi.schemers.org/srfi-25/play.scm">play.scm</a>, for playing around with the system.
|
|
|
|
</ol>
|
|
|
|
<H1>Implementation</H1>
|
|
|
|
<p>
|
|
A portable reference implementation is provided. It uses a minimal
|
|
error reporting mechanism that conforms to <a
|
|
href="http://srfi.schemers.org/srfi-23/">SRFI 23</a> (Error reporting
|
|
mechanism). Type disjointness requires support from the host
|
|
implementation, such as support for <a
|
|
href="http://srfi.schemers.org/srfi-9/">SRFI 9</a> (Defining Record
|
|
Types). All names not defined in this proposal are in the prefix
|
|
"<code>array:</code>", which serves as a module system.
|
|
</p>
|
|
|
|
<p>
|
|
You can get <a href="http://srfi.schemers.org/srfi-25/srfi-25-reference.scm">source for the reference
|
|
implementation</a> as a single file and stop reading. But there are
|
|
variations. This single file represents arrays as procedures (so the
|
|
type predicate is very approximate); it represents index mapping as
|
|
vectors of coefficients; map recognition is not optimised for any
|
|
number of dimensions as that would be redundant in this
|
|
representation.
|
|
</p>
|
|
|
|
<p>
|
|
The real source comes in too many files. A working installation
|
|
consists of the following parts, each in its own file.
|
|
</p>
|
|
|
|
<ol>
|
|
|
|
<li> a <dfn>record type definition</dfn>, either system specific for
|
|
type disjointness, or portable as procedures, in a file
|
|
<em>as-*.scm</em>, and
|
|
|
|
<li> <dfn>indexing operations</dfn> to match the type, in a file
|
|
<em>ix-*.scm</em>, and
|
|
|
|
<li> an <dfn>affine recogniser</dfn> of one of three types, optimised
|
|
up to some number of dimensions, in a file <em>op-*.scm</em>, and
|
|
|
|
<li> the main source file <a href="http://srfi.schemers.org/srfi-25/array.scm">array.scm</a>.
|
|
|
|
</ol>
|
|
|
|
<p>
|
|
Affine recognisers are made by a program <a href="http://srfi.schemers.org/srfi-25/opt.scm">opt.scm</a>
|
|
but one of each type is also available here, optimized for 0, 1, 2 and
|
|
3 dimensions. Choose one type: pick a recogniser with matching index
|
|
procedures; load <tt>as-</tt>, <tt>ix-</tt> and <tt>op-</tt> and
|
|
<tt>array</tt>.)
|
|
</p>
|
|
|
|
<ol>
|
|
|
|
<li> In the <a href="http://srfi.schemers.org/srfi-25/op-mbda.scm">mbda</a> type representation, index
|
|
mappings are procedures that accept an optional argument. The
|
|
matching <a href="http://srfi.schemers.org/srfi-25/ix-mbda.scm">access procedures</a> apply the
|
|
mapping to the arguments of <code>array-ref</code> and
|
|
<code>array-set!</code>.
|
|
|
|
<li> In the <a href="http://srfi.schemers.org/srfi-25/op-tter.scm">tter</a> type representation, index
|
|
mappings are pairs of procedures: one takes exactly the indices,
|
|
the other takes indices and an object. The matching <a
|
|
href="http://srfi.schemers.org/srfi-25/ix-tter.scm">access procedures</a> apply the first
|
|
procedure to the argumets of <code>array-ref</code> and the
|
|
second procedure to the arguments of <code>array-set!</code>.
|
|
|
|
<li> In <a href="http://srfi.schemers.org/srfi-25/op-ctor.scm">ctor</a> representation, index mappings
|
|
are coefficient vectors. The <a href="http://srfi.schemers.org/srfi-25/ix-ctor.scm">access
|
|
procedures</a> compute the sum of products of coefficients and
|
|
indexes in a loop on the list.
|
|
|
|
</ol>
|
|
|
|
<p>
|
|
Record implementations are available <a href="http://srfi.schemers.org/srfi-25/as-procedure.scm">for
|
|
generic Scheme</a> (arrays are not disjoint from procedures), <a
|
|
href="http://srfi.schemers.org/srfi-25/as-srfi-9-record.scm">for SRFI 9</a> (Defining Record Types)
|
|
(<em>not tested</em>), and <a href="http://srfi.schemers.org/srfi-25/as-plt-struct.scm">for PLT
|
|
Scheme</a> (arrays belong to a struct type).
|
|
</p>
|
|
|
|
<p>
|
|
With the three files from above, the <a href="http://srfi.schemers.org/srfi-25/array.scm">main source
|
|
file</a> should work in any Scheme implementation without need of
|
|
modification.
|
|
</p>
|
|
|
|
<p>
|
|
Error checking in the implementation may be a tad expensive. The
|
|
places where it occurs are cleanly separated from the surrounding
|
|
code. (Sharing uses a check that is exponential in the number of
|
|
dimensions. It is disabled above a threshold rank.)
|
|
</p>
|
|
|
|
<h1>Acknowledgements</h1>
|
|
|
|
<p>
|
|
The original concept comes from a message to the Usenet newsgroup
|
|
<tt>comp.lang.scheme</tt> by Alan Bawden in 1993. A variant of that
|
|
implementation by Richard Kelsey in the Scheme 48 system was also an
|
|
influence. Apart from the origins, the main design goal has been
|
|
consistency with the core types of Scheme.
|
|
</p>
|
|
|
|
<p>
|
|
Alan Bawden and Mark K. Gardner gave useful comments at an earlier
|
|
attempt to make this specification public. (There was at least one
|
|
other. Notes have gone missing.) SRFI feedback led to improved
|
|
wording, hidden shapes, and two kinds of index objects.
|
|
</p>
|
|
|
|
<p>
|
|
The exact title of the proposal comes from a <a href="http://zurich.ai.mit.edu/pipermail/rrrs-authors/1998-May/002349.html">message titled "a process that might work"</a> by William D. Clinger to the <tt>rrrs-authors</tt>
|
|
mailing list in 1998. That appears to be a part of the past of the <a
|
|
href="http://srfi.schemers.org">SRFI process</a>.
|
|
</p>
|
|
|
|
<H1>Copyright</H1>
|
|
<p>Copyright (C) Jussi Piitulainen (2001). All Rights Reserved.</p>
|
|
|
|
<p>
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
a copy of this software and associated documentation files (the
|
|
"Software"), to deal in the Software without restriction, including
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
the following conditions:
|
|
</p>
|
|
<p>
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
</p>
|
|
<p>
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
</p>
|
|
|
|
<hr>
|
|
<address>Editor: <a
|
|
href="mailto:srfi-editors@srfi.schemers.org">Mike Sperber</a></address>
|
|
<!-- Created: Tue Sep 29 19:20:08 EDT 1998 -->
|
|
<!-- hhmts start -->
|
|
Last modified: Tue May 28 18:46:09 MST 2002
|
|
<!-- hhmts end -->
|
|
</body>
|
|
</html>
|