|
Alternative array construction
John Cowan
(24 Mar 2026 03:52 UTC)
|
|
Re: Alternative array construction Bradley Lucier (24 Mar 2026 22:32 UTC)
|
|
Re: Alternative array construction
John Cowan
(25 Mar 2026 01:52 UTC)
|
On 3/23/26 23:52, John Cowan wrote:
>
> In discussion on #scheme tonight, the point was brought up that having
> to call make-interval to make an array is a bit verbose, and make-
> interval is problematic if you need a very-high-dimensional array.
>
> (make-array (make-interval #(1 1 1) #(3 3 3) ...) is one thing, but if
> there are 256 dimensions of various widths, it would be easy to make a
> mistake aligning the upper and lower bounds. I therefore suggest that
> make-array and make-specialized-array accept a nested list as well as an
> interval, thus allowing:
>
> (make-array '((1 3) (1 4) (1 5)) ...)
>
> Of course, the same would apply to make-specialized-array. Alternatively
> or additionally, make-interval could accept the same kind of nested list.
>
> I'm also planning to use this style in my forthcoming array-literal SRFI.
I agree, the current notation is clumsy.
I've come up with something similar to your suggestion, which I explain
below. I invite opinions about whether I should continue in this direction.
==========================
Sometimes you want an explicit call to make-interval (when an interval
is named and reused), so let's start with that.
Currently we allow
(make-interval vec1)
and
(make-interval vec1 vec2)
where vec1 and vec2 are vectors of exact integers, and if both are given
every component of vec1 is <= the corresponding component of vec2.
Currently, if only vec1 is given, we assume that all components are
nonnegative, and that vec1 gives the upper bounds of an interval.
Perhaps we can extend this to: If only vec1 is given, then each
component is either (a) a nonnegative exact integer u or (b) a list (l
u), with l and u exact integers with l <= u. In either case u is taken
to be the upper bound of that axis; in case (a) the lower bound is zero,
in case (b) the lower bound is l.
Some examples from the document rewritten in this style (using
quasi-quote to build the vector arguments to make-interval) are
(untested code):
Determining whether a string is a palindrome:
(define (palindrome? s)
;; Is the string s a palindrome, ignoring case
(let ((S
(make-specialized-array-from-data s char-storage-class))
(half-domain
(make-interval `#(,(quotient (string-length s) 2)))))
;; The two extracted arrays are empty if s consists of 0 or 1
characters
(array-every char-ci=?
;; the first half of S
(array-extract S half-domain)
;; the reversed second half of S
(array-extract (array-reverse S) half-domain))))
(for-each (lambda (s)
(for-each display
(list "(palindrome? \""
s
"\") => "
(palindrome? s)
#\newline)))
'("" "a" "aa" "ab" "aba" "abc" "abba" "abca" "abbc"
"AManAPlanACanalPanama"))
LU-decomposition:
(define (LU-decomposition A)
;; Assumes the domain of A is [0,n)\\times [0,n)
;; and that Gaussian elimination can be applied
;; without pivoting.
(let ((n
(interval-upper-bound (array-domain A) 0))
(A_
(array-getter A)))
(do ((i 0 (fx+ i 1)))
((= i (fx- n 1)) A)
(let* ((pivot
(A_ i i))
(column/row-domain
;; both will be one-dimensional
(make-interval `#((,(+ i 1) ,n))))
(column
;; the column below the (i,i) entry
(specialized-array-share A
column/row-domain
(lambda (k)
(values k i))))
(row
;; the row to the right of the (i,i) entry
(specialized-array-share A
column/row-domain
(lambda (k)
(values i k))))
;; the subarray to the right and
;; below the (i,i) entry
(subarray
(array-extract
A (interval-cartesian-product column/row-domain
column/row-domain))))
;; Compute multipliers.
(array-assign!
column
(array-map (lambda (x)
(/ x pivot))
column))
;; Subtract the outer product of i'th
;; row and column from the subarray.
(array-assign!
subarray
(array-map -
subarray
(array-outer-product * column row)))))))
This would be a compatible extension of the current syntax.
==============================
The other question is whether, in place of an interval argument to a
procedure, one could have that procedure accept a vector each of whose
components is either (a) a nonnegative exact integer, or (b) a list (l
u) whose elements are exact integers with l <= u; i.e., the type of
vector that is allowed as a single argument to make-interval.
An example from the SRFI document:
Moving row k to the top (in the example k is 2):
(let* ((a (make-array (make-interval '#(4 6)) list))
(k 2)
(m (interval-upper-bound (array-domain a) 0))
(n (interval-upper-bound (array-domain a) 1)))
(pretty-print
(array->list* a))
(newline)
(pretty-print
(array->list*
(array-append
0
(list (array-extract a `#((,k ,(+ k 1)) ,n))
(array-extract a `#(,k ,n))
(array-extract a `#((,(k+1) ,m) ,n)))))))
This would seem to be a reasonable extension to the current SRFI.
Brad