63.body.scm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. ;;;;"array.scm" Arrays for Scheme
  2. ; Copyright (C) 2001, 2003 Aubrey Jaffer
  3. ;
  4. ;Permission to copy this software, to modify it, to redistribute it,
  5. ;to distribute modified versions, and to use it for any purpose is
  6. ;granted, subject to the following restrictions and understandings.
  7. ;
  8. ;1. Any copy made of this software must include this copyright notice
  9. ;in full.
  10. ;
  11. ;2. I have made no warranty or representation that the operation of
  12. ;this software will be error-free, and I am under no obligation to
  13. ;provide any services, by way of maintenance, update, or otherwise.
  14. ;
  15. ;3. In conjunction with products arising from the use of this
  16. ;material, there shall be no use of my name in any advertising,
  17. ;promotional, or sales literature without prior written consent in
  18. ;each case.
  19. ;;@code{(require 'array)} or @code{(require 'srfi-63)}
  20. ;;@ftindex array
  21. (define-record-type <array>
  22. (array:construct dimensions scales offset store)
  23. array:array?
  24. (dimensions dimensions)
  25. (scales scales)
  26. (offset offset)
  27. (store store))
  28. (define (array:dimensions array)
  29. (cond ((vector? array) (list (vector-length array)))
  30. ((string? array) (list (string-length array)))
  31. (else (dimensions array))))
  32. (define (array:scales array)
  33. (cond ((vector? array) '(1))
  34. ((string? array) '(1))
  35. (else (scales array))))
  36. (define (array:store array)
  37. (cond ((vector? array) array)
  38. ((string? array) array)
  39. (else (store array))))
  40. (define (array:offset array)
  41. (cond ((vector? array) 0)
  42. ((string? array) 0)
  43. (else (offset array))))
  44. ;;@args obj
  45. ;;Returns @code{#t} if the @1 is an array, and @code{#f} if not.
  46. (define (array? obj)
  47. (or (vector? obj) (string? obj) (array:array? obj)))
  48. ;;@noindent
  49. ;;@emph{Note:} Arrays are not disjoint from other Scheme types.
  50. ;;Vectors and possibly strings also satisfy @code{array?}.
  51. ;;A disjoint array predicate can be written:
  52. ;;
  53. ;;@example
  54. ;;(define (strict-array? obj)
  55. ;; (and (array? obj) (not (string? obj)) (not (vector? obj))))
  56. ;;@end example
  57. ;;@body
  58. ;;Returns @code{#t} if @1 and @2 have the same rank and dimensions and the
  59. ;;corresponding elements of @1 and @2 are @code{equal?}.
  60. ;;@body
  61. ;;@0 recursively compares the contents of pairs, vectors, strings, and
  62. ;;@emph{arrays}, applying @code{eqv?} on other objects such as numbers
  63. ;;and symbols. A rule of thumb is that objects are generally @0 if
  64. ;;they print the same. @0 may fail to terminate if its arguments are
  65. ;;circular data structures.
  66. ;;
  67. ;;@example
  68. ;;(equal? 'a 'a) @result{} #t
  69. ;;(equal? '(a) '(a)) @result{} #t
  70. ;;(equal? '(a (b) c)
  71. ;; '(a (b) c)) @result{} #t
  72. ;;(equal? "abc" "abc") @result{} #t
  73. ;;(equal? 2 2) @result{} #t
  74. ;;(equal? (make-vector 5 'a)
  75. ;; (make-vector 5 'a)) @result{} #t
  76. ;;(equal? (make-array (a:fixN32b 4) 5 3)
  77. ;; (make-array (a:fixN32b 4) 5 3)) @result{} #t
  78. ;;(equal? (make-array '#(foo) 3 3)
  79. ;; (make-array '#(foo) 3 3)) @result{} #t
  80. ;;(equal? (lambda (x) x)
  81. ;; (lambda (y) y)) @result{} @emph{unspecified}
  82. ;;@end example
  83. (define (equal? obj1 obj2)
  84. (cond ((eqv? obj1 obj2) #t)
  85. ((or (pair? obj1) (pair? obj2))
  86. (and (pair? obj1) (pair? obj2)
  87. (equal? (car obj1) (car obj2))
  88. (equal? (cdr obj1) (cdr obj2))))
  89. ((or (string? obj1) (string? obj2))
  90. (and (string? obj1) (string? obj2)
  91. (string=? obj1 obj2)))
  92. ((or (vector? obj1) (vector? obj2))
  93. (and (vector? obj1) (vector? obj2)
  94. (equal? (vector-length obj1) (vector-length obj2))
  95. (do ((idx (+ -1 (vector-length obj1)) (+ -1 idx)))
  96. ((or (negative? idx)
  97. (not (equal? (vector-ref obj1 idx)
  98. (vector-ref obj2 idx))))
  99. (negative? idx)))))
  100. ((or (array? obj1) (array? obj2))
  101. (and (array? obj1) (array? obj2)
  102. (equal? (array:dimensions obj1) (array:dimensions obj2))
  103. (equal? (array:store obj1) (array:store obj2))))
  104. (else #f)))
  105. ;;@body
  106. ;;Returns the number of dimensions of @1. If @1 is not an array, 0 is
  107. ;;returned.
  108. (define (array-rank obj)
  109. (if (array? obj) (length (array:dimensions obj)) 0))
  110. ;;@args array
  111. ;;Returns a list of dimensions.
  112. ;;
  113. ;;@example
  114. ;;(array-dimensions (make-array '#() 3 5))
  115. ;; @result{} (3 5)
  116. ;;@end example
  117. (define array-dimensions array:dimensions)
  118. ;;@args prototype k1 @dots{}
  119. ;;
  120. ;;Creates and returns an array of type @1 with dimensions @2, @dots{}
  121. ;;and filled with elements from @1. @1 must be an array, vector, or
  122. ;;string. The implementation-dependent type of the returned array
  123. ;;will be the same as the type of @1; except if that would be a vector
  124. ;;or string with rank not equal to one, in which case some variety of
  125. ;;array will be returned.
  126. ;;
  127. ;;If the @1 has no elements, then the initial contents of the returned
  128. ;;array are unspecified. Otherwise, the returned array will be filled
  129. ;;with the element at the origin of @1.
  130. (define (make-array prototype . dimensions)
  131. (define tcnt (apply * dimensions))
  132. (let ((store
  133. (if (string? prototype)
  134. (case (string-length prototype)
  135. ((0) (make-string tcnt))
  136. (else (make-string tcnt
  137. (string-ref prototype 0))))
  138. (let ((pdims (array:dimensions prototype)))
  139. (case (apply * pdims)
  140. ((0) (make-vector tcnt))
  141. (else (make-vector tcnt
  142. (apply array-ref prototype
  143. (map (lambda (x) 0) pdims)))))))))
  144. (define (loop dims scales)
  145. (if (null? dims)
  146. (array:construct dimensions (cdr scales) 0 store)
  147. (loop (cdr dims) (cons (* (car dims) (car scales)) scales))))
  148. (loop (reverse dimensions) '(1))))
  149. ;;@args prototype k1 @dots{}
  150. ;;@0 is an alias for @code{make-array}.
  151. (define create-array make-array)
  152. ;;@args array mapper k1 @dots{}
  153. ;;@0 can be used to create shared subarrays of other
  154. ;;arrays. The @var{mapper} is a function that translates coordinates in
  155. ;;the new array into coordinates in the old array. A @var{mapper} must be
  156. ;;linear, and its range must stay within the bounds of the old array, but
  157. ;;it can be otherwise arbitrary. A simple example:
  158. ;;
  159. ;;@example
  160. ;;(define fred (make-array '#(#f) 8 8))
  161. ;;(define freds-diagonal
  162. ;; (make-shared-array fred (lambda (i) (list i i)) 8))
  163. ;;(array-set! freds-diagonal 'foo 3)
  164. ;;(array-ref fred 3 3)
  165. ;; @result{} FOO
  166. ;;(define freds-center
  167. ;; (make-shared-array fred (lambda (i j) (list (+ 3 i) (+ 3 j)))
  168. ;; 2 2))
  169. ;;(array-ref freds-center 0 0)
  170. ;; @result{} FOO
  171. ;;@end example
  172. (define (make-shared-array array mapper . dimensions)
  173. (define odl (array:scales array))
  174. (define rank (length dimensions))
  175. (define shape
  176. (map (lambda (dim) (if (list? dim) dim (list 0 (+ -1 dim)))) dimensions))
  177. (do ((idx (+ -1 rank) (+ -1 idx))
  178. (uvt (append (cdr (vector->list (make-vector rank 0))) '(1))
  179. (append (cdr uvt) '(0)))
  180. (uvts '() (cons uvt uvts)))
  181. ((negative? idx)
  182. (let ((ker0 (apply + (map * odl (apply mapper uvt)))))
  183. (array:construct
  184. (map (lambda (dim) (+ 1 (- (cadr dim) (car dim)))) shape)
  185. (map (lambda (uvt) (- (apply + (map * odl (apply mapper uvt))) ker0))
  186. uvts)
  187. (apply +
  188. (array:offset array)
  189. (map * odl (apply mapper (map car shape))))
  190. (array:store array))))))
  191. ;;@args rank proto list
  192. ;;@3 must be a rank-nested list consisting of all the elements, in
  193. ;;row-major order, of the array to be created.
  194. ;;
  195. ;;@0 returns an array of rank @1 and type @2 consisting of all the
  196. ;;elements, in row-major order, of @3. When @1 is 0, @3 is the lone
  197. ;;array element; not necessarily a list.
  198. ;;
  199. ;;@example
  200. ;;(list->array 2 '#() '((1 2) (3 4)))
  201. ;; @result{} #2A((1 2) (3 4))
  202. ;;(list->array 0 '#() 3)
  203. ;; @result{} #0A 3
  204. ;;@end example
  205. (define (list->array rank proto lst)
  206. (define dimensions
  207. (do ((shp '() (cons (length row) shp))
  208. (row lst (car lst))
  209. (rnk (+ -1 rank) (+ -1 rnk)))
  210. ((negative? rnk) (reverse shp))))
  211. (let ((nra (apply make-array proto dimensions)))
  212. (define (l2ra dims idxs row)
  213. (cond ((null? dims)
  214. (apply array-set! nra row (reverse idxs)))
  215. (else
  216. (if (not (eqv? (car dims) (length row)))
  217. (error "Array not rectangular:" dims dimensions))
  218. (do ((idx 0 (+ 1 idx))
  219. (row row (cdr row)))
  220. ((>= idx (car dims)))
  221. (l2ra (cdr dims) (cons idx idxs) (car row))))))
  222. (l2ra dimensions '() lst)
  223. nra))
  224. ;;@args array
  225. ;;Returns a rank-nested list consisting of all the elements, in
  226. ;;row-major order, of @1. In the case of a rank-0 array, @0 returns
  227. ;;the single element.
  228. ;;
  229. ;;@example
  230. ;;(array->list #2A((ho ho ho) (ho oh oh)))
  231. ;; @result{} ((ho ho ho) (ho oh oh))
  232. ;;(array->list #0A ho)
  233. ;; @result{} ho
  234. ;;@end example
  235. (define (array->list ra)
  236. (define (ra2l dims idxs)
  237. (if (null? dims)
  238. (apply array-ref ra (reverse idxs))
  239. (do ((lst '() (cons (ra2l (cdr dims) (cons idx idxs)) lst))
  240. (idx (+ -1 (car dims)) (+ -1 idx)))
  241. ((negative? idx) lst))))
  242. (ra2l (array-dimensions ra) '()))
  243. ;;@args vect proto dim1 @dots{}
  244. ;;@1 must be a vector of length equal to the product of exact
  245. ;;nonnegative integers @3, @dots{}.
  246. ;;
  247. ;;@0 returns an array of type @2 consisting of all the elements, in
  248. ;;row-major order, of @1. In the case of a rank-0 array, @1 has a
  249. ;;single element.
  250. ;;
  251. ;;@example
  252. ;;(vector->array #(1 2 3 4) #() 2 2)
  253. ;; @result{} #2A((1 2) (3 4))
  254. ;;(vector->array '#(3) '#())
  255. ;; @result{} #0A 3
  256. ;;@end example
  257. (define (vector->array vect prototype . dimensions)
  258. (define vdx (vector-length vect))
  259. (if (not (eqv? vdx (apply * dimensions)))
  260. (error "Vector length does not equal product of dimensions:"
  261. vdx dimensions))
  262. (let ((ra (apply make-array prototype dimensions)))
  263. (define (v2ra dims idxs)
  264. (cond ((null? dims)
  265. (set! vdx (+ -1 vdx))
  266. (apply array-set! ra (vector-ref vect vdx) (reverse idxs)))
  267. (else
  268. (do ((idx (+ -1 (car dims)) (+ -1 idx)))
  269. ((negative? idx) vect)
  270. (v2ra (cdr dims) (cons idx idxs))))))
  271. (v2ra dimensions '())
  272. ra))
  273. ;;@args array
  274. ;;Returns a new vector consisting of all the elements of @1 in
  275. ;;row-major order.
  276. ;;
  277. ;;@example
  278. ;;(array->vector #2A ((1 2)( 3 4)))
  279. ;; @result{} #(1 2 3 4)
  280. ;;(array->vector #0A ho)
  281. ;; @result{} #(ho)
  282. ;;@end example
  283. (define (array->vector ra)
  284. (define dims (array-dimensions ra))
  285. (let* ((vdx (apply * dims))
  286. (vect (make-vector vdx)))
  287. (define (ra2v dims idxs)
  288. (if (null? dims)
  289. (let ((val (apply array-ref ra (reverse idxs))))
  290. (set! vdx (+ -1 vdx))
  291. (vector-set! vect vdx val)
  292. vect)
  293. (do ((idx (+ -1 (car dims)) (+ -1 idx)))
  294. ((negative? idx) vect)
  295. (ra2v (cdr dims) (cons idx idxs)))))
  296. (ra2v dims '())))
  297. (define (array:in-bounds? array indices)
  298. (do ((bnds (array:dimensions array) (cdr bnds))
  299. (idxs indices (cdr idxs)))
  300. ((or (null? bnds)
  301. (null? idxs)
  302. (not (integer? (car idxs)))
  303. (not (< -1 (car idxs) (car bnds))))
  304. (and (null? bnds) (null? idxs)))))
  305. ;;@args array index1 @dots{}
  306. ;;Returns @code{#t} if its arguments would be acceptable to
  307. ;;@code{array-ref}.
  308. (define (array-in-bounds? array . indices)
  309. (array:in-bounds? array indices))
  310. ;;@args array k1 @dots{}
  311. ;;Returns the (@2, @dots{}) element of @1.
  312. (define (array-ref array . indices)
  313. (define store (array:store array))
  314. (or (array:in-bounds? array indices)
  315. (error "Bad indices:" indices))
  316. ((if (string? store) string-ref vector-ref)
  317. store (apply + (array:offset array) (map * (array:scales array) indices))))
  318. ;;@args array obj k1 @dots{}
  319. ;;Stores @2 in the (@3, @dots{}) element of @1. The value returned
  320. ;;by @0 is unspecified.
  321. (define (array-set! array obj . indices)
  322. (define store (array:store array))
  323. (or (array:in-bounds? array indices)
  324. (error "Bad indices:" indices))
  325. ((if (string? store) string-set! vector-set!)
  326. store (apply + (array:offset array) (map * (array:scales array) indices))
  327. obj))
  328. ;;@noindent
  329. ;;These functions return a prototypical uniform-array enclosing the
  330. ;;optional argument (which must be of the correct type). If the
  331. ;;uniform-array type is supported by the implementation, then it is
  332. ;;returned; defaulting to the next larger precision type; resorting
  333. ;;finally to vector.
  334. (define (make-prototype-checker name pred? creator)
  335. (lambda args
  336. (case (length args)
  337. ((1) (if (pred? (car args))
  338. (creator (car args))
  339. (error "Incompatible type:" name (car args))))
  340. ((0) (creator))
  341. (else (error "Wrong number of arguments:" name args)))))
  342. (define (integer-bytes?? n)
  343. (lambda (obj)
  344. (and (integer? obj)
  345. (exact? obj)
  346. (or (negative? n) (not (negative? obj)))
  347. (do ((num obj (quotient num 256))
  348. (n (+ -1 (abs n)) (+ -1 n)))
  349. ((or (zero? num) (negative? n))
  350. (zero? num))))))
  351. ;;@args z
  352. ;;@args
  353. ;;Returns an inexact 128.bit flonum complex uniform-array prototype.
  354. (define a:floc128b (make-prototype-checker 'a:floc128b complex? vector))
  355. ;;@args z
  356. ;;@args
  357. ;;Returns an inexact 64.bit flonum complex uniform-array prototype.
  358. (define a:floc64b (make-prototype-checker 'a:floc64b complex? vector))
  359. ;;@args z
  360. ;;@args
  361. ;;Returns an inexact 32.bit flonum complex uniform-array prototype.
  362. (define a:floc32b (make-prototype-checker 'a:floc32b complex? vector))
  363. ;;@args z
  364. ;;@args
  365. ;;Returns an inexact 16.bit flonum complex uniform-array prototype.
  366. (define a:floc16b (make-prototype-checker 'a:floc16b complex? vector))
  367. ;;@args z
  368. ;;@args
  369. ;;Returns an inexact 128.bit flonum real uniform-array prototype.
  370. (define a:flor128b (make-prototype-checker 'a:flor128b real? vector))
  371. ;;@args z
  372. ;;@args
  373. ;;Returns an inexact 64.bit flonum real uniform-array prototype.
  374. (define a:flor64b (make-prototype-checker 'a:flor64b real? vector))
  375. ;;@args z
  376. ;;@args
  377. ;;Returns an inexact 32.bit flonum real uniform-array prototype.
  378. (define a:flor32b (make-prototype-checker 'a:flor32b real? vector))
  379. ;;@args z
  380. ;;@args
  381. ;;Returns an inexact 16.bit flonum real uniform-array prototype.
  382. (define a:flor16b (make-prototype-checker 'a:flor16b real? vector))
  383. ;;@args z
  384. ;;@args
  385. ;;Returns an exact 128.bit decimal flonum rational uniform-array prototype.
  386. (define a:flor128b (make-prototype-checker 'a:flor128b real? vector))
  387. ;;@args z
  388. ;;@args
  389. ;;Returns an exact 64.bit decimal flonum rational uniform-array prototype.
  390. (define a:flor64b (make-prototype-checker 'a:flor64b real? vector))
  391. ;;@args z
  392. ;;@args
  393. ;;Returns an exact 32.bit decimal flonum rational uniform-array prototype.
  394. (define a:flor32b (make-prototype-checker 'a:flor32b real? vector))
  395. ;;@args n
  396. ;;@args
  397. ;;Returns an exact binary fixnum uniform-array prototype with at least
  398. ;;64 bits of precision.
  399. (define a:fixz64b (make-prototype-checker 'a:fixz64b (integer-bytes?? -8) vector))
  400. ;;@args n
  401. ;;@args
  402. ;;Returns an exact binary fixnum uniform-array prototype with at least
  403. ;;32 bits of precision.
  404. (define a:fixz32b (make-prototype-checker 'a:fixz32b (integer-bytes?? -4) vector))
  405. ;;@args n
  406. ;;@args
  407. ;;Returns an exact binary fixnum uniform-array prototype with at least
  408. ;;16 bits of precision.
  409. (define a:fixz16b (make-prototype-checker 'a:fixz16b (integer-bytes?? -2) vector))
  410. ;;@args n
  411. ;;@args
  412. ;;Returns an exact binary fixnum uniform-array prototype with at least
  413. ;;8 bits of precision.
  414. (define a:fixz8b (make-prototype-checker 'a:fixz8b (integer-bytes?? -1) vector))
  415. ;;@args k
  416. ;;@args
  417. ;;Returns an exact non-negative binary fixnum uniform-array prototype with at
  418. ;;least 64 bits of precision.
  419. (define a:fixn64b (make-prototype-checker 'a:fixn64b (integer-bytes?? 8) vector))
  420. ;;@args k
  421. ;;@args
  422. ;;Returns an exact non-negative binary fixnum uniform-array prototype with at
  423. ;;least 32 bits of precision.
  424. (define a:fixn32b (make-prototype-checker 'a:fixn32b (integer-bytes?? 4) vector))
  425. ;;@args k
  426. ;;@args
  427. ;;Returns an exact non-negative binary fixnum uniform-array prototype with at
  428. ;;least 16 bits of precision.
  429. (define a:fixn16b (make-prototype-checker 'a:fixn16b (integer-bytes?? 2) vector))
  430. ;;@args k
  431. ;;@args
  432. ;;Returns an exact non-negative binary fixnum uniform-array prototype with at
  433. ;;least 8 bits of precision.
  434. (define a:fixn8b (make-prototype-checker 'a:fixn8b (integer-bytes?? 1) vector))
  435. ;;@args bool
  436. ;;@args
  437. ;;Returns a boolean uniform-array prototype.
  438. (define a:bool (make-prototype-checker 'a:bool boolean? vector))