Now that you write out the definitions, I see why they work, but I certainly couldn't have come up with them on the fly. The whole thing kind of reminds me of K idioms, which are like APL idioms, only even harder because K has both ad hoc arity overloading and ad hoc type overloading. For example, unary ! is iota, binary ! on numbers is modulo, and binary ! on a list and a number is list rotation.)
For what it is worth, one could express
(array-map + a1 a2)
as:
(array-copy
(build-array (array-shape a1)
(lambda (I)
(+ (array-ref a1 I) (array-ref a2 I))))))
This uses array-copy, which could be defined as:
(define (array-copy arr)
(array-reshape (array-flatten arr) (array-shape arr)))
Using array-as-procedure and Kawa argument splicing:
(array-copy
(build-array (array-shape a1)
(lambda (I)
(+ (a1 @I) (a2 @I))))))
Using array-as-procedure and Kawa pattern matching:
(build-array (array-shape a1)
(lambda ([i j])
(+ (a1 i j) (a2 i j))))))
This is not to argue that array-map doesn't belong in an
arraya API (I think it does), but to show that it is only
a modest reduction in boilerplate. And using build-array
is obviously more flexible. Performance-wise (and assuming no
non-trivial optimizations) build-array is probably more expensive
than a single array-map, but is probably cheaper than multiple
array-maps. (You can often use build-array to manually merge
multiple array-maps.)
--
--Per Bothner
xxxxxx@bothner.com http://per.bothner.com/