Changed how array strictness works: `array-strict' now makes the

original array strict instead of returning a new strict array.
(Finally!) The hard part is keeping the Array type covariant. The
solution is to keep the store in the closure of the array's
procedure instead of in the Array struct itself.
This commit is contained in:
Neil Toronto 2012-11-29 22:12:45 -09:00
parent d7163142be
commit e4f5a0598f
12 changed files with 134 additions and 98 deletions

View File

@ -18,10 +18,7 @@
(: print-array (All (A) ((Array A) Symbol Output-Port (U #t #f 0 1) -> Any))) (: print-array (All (A) ((Array A) Symbol Output-Port (U #t #f 0 1) -> Any)))
;; The logic in `print-array' causes the REPL printer to try printing an array in each layout, and ;; The logic in `print-array' causes the REPL printer to try printing an array in each layout, and
;; keep the first successful one. An overflowing line means failure. ;; keep the first successful one. An overflowing line means failure.
(define (print-array orig-arr name port mode) (define (print-array arr name port mode)
;; Try to compute each element only once
(define arr (array-lazy orig-arr))
;; Called to print array elements; may recur (e.g. printing arrays of arrays) ;; Called to print array elements; may recur (e.g. printing arrays of arrays)
;; We never have to consider the `mode' argument again after defining `recur-print' ;; We never have to consider the `mode' argument again after defining `recur-print'
(define recur-print (define recur-print

View File

@ -4,17 +4,17 @@
typed/racket/base typed/racket/base
(for-syntax racket/base syntax/parse) (for-syntax racket/base syntax/parse)
"array-syntax.rkt" "array-syntax.rkt"
(except-in "typed-array-struct.rkt" build-array)) (except-in "typed-array-struct.rkt" build-array build-strict-array))
(require/untyped-contract (require/untyped-contract
(begin (require "typed-array-struct.rkt")) (begin (require "typed-array-struct.rkt"))
"typed-array-struct.rkt" "typed-array-struct.rkt"
[build-array (All (A) ((Vectorof Integer) ((Vectorof Index) -> A) -> (Array A)))]) [build-array (All (A) ((Vectorof Integer) ((Vectorof Index) -> A) -> (Array A)))]
[build-strict-array (All (A) ((Vectorof Integer) ((Vectorof Index) -> A) -> (Array A)))])
(define-syntax array? (make-rename-transformer #'Array?)) (define-syntax array? (make-rename-transformer #'Array?))
(define-syntax array-shape (make-rename-transformer #'Array-shape)) (define-syntax array-shape (make-rename-transformer #'Array-shape))
(define-syntax array-size (make-rename-transformer #'Array-size)) (define-syntax array-size (make-rename-transformer #'Array-size))
(define-syntax array-strict? (make-rename-transformer #'Array-strict?))
(define-syntax unsafe-array-proc (make-rename-transformer #'Array-unsafe-proc)) (define-syntax unsafe-array-proc (make-rename-transformer #'Array-unsafe-proc))
(provide (provide
@ -24,10 +24,13 @@
array-shape array-shape
array-dims array-dims
array-size array-size
array-strict
array-strict!
array-strict? array-strict?
make-unsafe-array-proc make-unsafe-array-proc
build-array build-array
unsafe-build-array unsafe-build-array
unsafe-build-strict-array
unsafe-array-proc unsafe-array-proc
array-lazy array-lazy
array array
@ -44,6 +47,10 @@
(define-syntax (array stx) (define-syntax (array stx)
(syntax-parse stx (syntax-parse stx
[(_ e:expr) [(_ e:expr) (syntax/loc stx (array/syntax array list flat-list->array e))]
(syntax/loc stx (array/syntax array list flat-list->array e))]
[_:id (raise-syntax-error 'array "not allowed as an expression" stx)])) [_:id (raise-syntax-error 'array "not allowed as an expression" stx)]))
(define-syntax-rule (array-strict arr-expr)
(let ([arr arr-expr])
(array-strict! arr)
arr))

View File

@ -32,7 +32,7 @@
Float-Complex ds (λ (j v) Float-Complex ds (λ (j v)
(unsafe-flvector-set! xs j (real-part v)) (unsafe-flvector-set! xs j (real-part v))
(unsafe-flvector-set! ys j (imag-part v))))) (unsafe-flvector-set! ys j (imag-part v)))))
(fcarray ds 0 #t proc set-proc xs ys)) (fcarray ds (flvector-length xs) (box #t) void proc set-proc xs ys))
(: unsafe-vector->fcarray (Indexes (Vectorof Number) -> FCArray)) (: unsafe-vector->fcarray (Indexes (Vectorof Number) -> FCArray))
(define (unsafe-vector->fcarray ds zs) (define (unsafe-vector->fcarray ds zs)

View File

@ -25,7 +25,7 @@
(define (unsafe-flarray ds vs) (define (unsafe-flarray ds vs)
(define proc (make-unsafe-array-proc ds (λ (j) (unsafe-flvector-ref vs j)))) (define proc (make-unsafe-array-proc ds (λ (j) (unsafe-flvector-ref vs j))))
(define set-proc (make-unsafe-array-set-proc Float ds (λ (j v) (unsafe-flvector-set! vs j v)))) (define set-proc (make-unsafe-array-set-proc Float ds (λ (j v) (unsafe-flvector-set! vs j v))))
(flarray ds 0 #t proc set-proc vs)) (flarray ds (flvector-length vs) (box #t) void proc set-proc vs))
(: unsafe-vector->flarray (Indexes (Vectorof Real) -> FlArray)) (: unsafe-vector->flarray (Indexes (Vectorof Real) -> FlArray))
(define (unsafe-vector->flarray ds vs) (define (unsafe-vector->flarray ds vs)

View File

@ -23,7 +23,6 @@
mutable-array mutable-array
;; Conversion ;; Conversion
array->mutable-array array->mutable-array
array-strict
flat-vector->matrix) flat-vector->matrix)
(define-syntax (mutable-array stx) (define-syntax (mutable-array stx)

View File

@ -10,7 +10,7 @@
(define (make-array ds v) (define (make-array ds v)
(let ([ds (check-array-shape (let ([ds (check-array-shape
ds (λ () (raise-argument-error 'make-array "(Vectorof Index)" 0 ds v)))]) ds (λ () (raise-argument-error 'make-array "(Vectorof Index)" 0 ds v)))])
(unsafe-build-array ds (λ (js) v)))) (unsafe-build-strict-array ds (λ (js) v))))
(: axis-index-array (In-Indexes Integer -> (Array Index))) (: axis-index-array (In-Indexes Integer -> (Array Index)))
(define (axis-index-array ds k) (define (axis-index-array ds k)
@ -18,21 +18,21 @@
ds (λ () (raise-argument-error 'axis-index-array "(Vectorof Index)" 0 ds k)))] ds (λ () (raise-argument-error 'axis-index-array "(Vectorof Index)" 0 ds k)))]
[dims (vector-length ds)]) [dims (vector-length ds)])
(cond [(and (0 . <= . k) (k . < . dims)) (cond [(and (0 . <= . k) (k . < . dims))
(unsafe-build-array ds (λ: ([js : Indexes]) (unsafe-vector-ref js k)))] (unsafe-build-strict-array ds (λ: ([js : Indexes]) (unsafe-vector-ref js k)))]
[else (raise-argument-error 'axis-index-array (format "Index < ~a" dims) 1 ds k)]))) [else (raise-argument-error 'axis-index-array (format "Index < ~a" dims) 1 ds k)])))
(: index-array (In-Indexes -> (Array Index))) (: index-array (In-Indexes -> (Array Index)))
(define (index-array ds) (define (index-array ds)
(let ([ds (check-array-shape (let ([ds (check-array-shape
ds (λ () (raise-argument-error 'index-array "(Vectorof Index)" ds)))]) ds (λ () (raise-argument-error 'index-array "(Vectorof Index)" ds)))])
(unsafe-build-array ds (λ: ([js : Indexes]) (unsafe-build-strict-array ds (λ: ([js : Indexes])
(assert (unsafe-array-index->value-index ds js) index?))))) (assert (unsafe-array-index->value-index ds js) index?)))))
(: indexes-array (In-Indexes -> (Array Indexes))) (: indexes-array (In-Indexes -> (Array Indexes)))
(define (indexes-array ds) (define (indexes-array ds)
(let ([ds (check-array-shape (let ([ds (check-array-shape
ds (λ () (raise-argument-error 'indexes-array "(Vectorof Index)" ds)))]) ds (λ () (raise-argument-error 'indexes-array "(Vectorof Index)" ds)))])
(unsafe-build-array ds (λ: ([js : Indexes]) (vector-copy-all js))))) (unsafe-build-strict-array ds (λ: ([js : Indexes]) (vector-copy-all js)))))
(: diagonal-array (All (A) (Integer Integer A A -> (Array A)))) (: diagonal-array (All (A) (Integer Integer A A -> (Array A))))
(define (diagonal-array dims size on-value off-value) (define (diagonal-array dims size on-value off-value)
@ -42,15 +42,15 @@
(define: ds : Indexes (make-vector dims size)) (define: ds : Indexes (make-vector dims size))
;; specialize for various cases ;; specialize for various cases
(cond [(or (dims . <= . 1) (size . <= . 1)) (cond [(or (dims . <= . 1) (size . <= . 1))
(unsafe-build-array ds (λ: ([js : Indexes]) on-value))] (unsafe-build-strict-array ds (λ: ([js : Indexes]) on-value))]
[(= dims 2) [(= dims 2)
(unsafe-build-array (unsafe-build-strict-array
ds (λ: ([js : Indexes]) ds (λ: ([js : Indexes])
(define j0 (unsafe-vector-ref js 0)) (define j0 (unsafe-vector-ref js 0))
(define j1 (unsafe-vector-ref js 1)) (define j1 (unsafe-vector-ref js 1))
(if (= j0 j1) on-value off-value)))] (if (= j0 j1) on-value off-value)))]
[else [else
(unsafe-build-array (unsafe-build-strict-array
ds (λ: ([js : Indexes]) ds (λ: ([js : Indexes])
(define j0 (unsafe-vector-ref js 0)) (define j0 (unsafe-vector-ref js 0))
(let: loop : A ([i : Nonnegative-Fixnum 1]) (let: loop : A ([i : Nonnegative-Fixnum 1])

View File

@ -37,16 +37,6 @@
;; =================================================================================================== ;; ===================================================================================================
;; Array data type: a function whose domain has a rectangular shape ;; Array data type: a function whose domain has a rectangular shape
(: array-guard (All (A) (Indexes Index Boolean (Indexes -> A) Symbol
-> (Values Indexes Index Boolean (Indexes -> A)))))
(define (array-guard ds size strict? proc name)
(cond [(zero? size)
(let ([size (array-shape-size ds)])
(cond [(index? size) (values (vector->immutable-vector ds) size strict? proc)]
[else (error 'array "array size ~e (for shape ~e) is too large (is not an Index)"
size ds)]))]
[else (values (vector->immutable-vector ds) size strict? proc)]))
#| #|
(: array-procedure (All (A) ((Array A) In-Indexes -> A))) (: array-procedure (All (A) ((Array A) In-Indexes -> A)))
(define (array-procedure arr js) (define (array-procedure arr js)
@ -55,9 +45,9 @@
(struct: (A) Array ([shape : Indexes] (struct: (A) Array ([shape : Indexes]
[size : Index] [size : Index]
[strict? : Boolean] [strict? : (Boxof Boolean)]
[strict! : (-> Void)]
[unsafe-proc : (Indexes -> A)]) [unsafe-proc : (Indexes -> A)])
#:guard array-guard
#:property prop:custom-print-quotable 'never #:property prop:custom-print-quotable 'never
#:property prop:custom-write (λ (arr port mode) ((array-custom-printer) arr 'array port mode)) #:property prop:custom-write (λ (arr port mode) ((array-custom-printer) arr 'array port mode))
#:property prop:equal+hash (list array-recur-equal? array-hash-code array-hash-code) #:property prop:equal+hash (list array-recur-equal? array-hash-code array-hash-code)
@ -70,42 +60,79 @@
(ref (unsafe-array-index->value-index ds js)))) (ref (unsafe-array-index->value-index ds js))))
(: array-dims (All (A) ((Array A) -> Index))) (: array-dims (All (A) ((Array A) -> Index)))
(begin-encourage-inline (define (array-dims arr)
(define (array-dims arr) (vector-length (Array-shape arr)))) (vector-length (Array-shape arr)))
(: array-strict? (All (A) ((Array A) -> Boolean)))
(define (array-strict? arr)
(unbox (Array-strict? arr)))
(: array-strict! (All (A) ((Array A) -> Void)))
(define (array-strict! arr)
(define strict? (Array-strict? arr))
(unless (unbox strict?)
((Array-strict! arr))
(set-box! strict? #t)))
(: unsafe-build-array (All (A) (Indexes (Indexes -> A) -> (Array A))))
(define (unsafe-build-array ds f)
(define size (check-array-shape-size 'unsafe-build-array ds))
(define: data : (U #f (Vectorof A)) #f)
(define (strict!)
(set! data (inline-build-array-data ds (λ (js j) (f js)) A)))
(define unsafe-proc
(λ: ([js : Indexes])
(let ([data data])
(if data
(unsafe-vector-ref data (unsafe-array-index->value-index ds js))
(f js)))))
(Array ds size ((inst box Boolean) #f) strict! unsafe-proc))
(: unsafe-build-strict-array (All (A) (Indexes (Indexes -> A) -> (Array A))))
(define (unsafe-build-strict-array ds f)
(define size (check-array-shape-size 'unsafe-build-strict-array ds))
(Array ds size (box #t) void f))
(: build-array (All (A) (In-Indexes (Indexes -> A) -> (Array A)))) (: build-array (All (A) (In-Indexes (Indexes -> A) -> (Array A))))
(define (build-array ds proc) (define (build-array ds proc)
(let ([ds (check-array-shape (let ([ds (check-array-shape
ds (λ () (raise-argument-error 'build-array "(Vectorof Index)" 0 ds proc)))]) ds (λ () (raise-argument-error 'build-array "(Vectorof Index)" 0 ds proc)))])
(Array ds 0 #f (λ: ([js : Indexes]) (unsafe-build-array ds (λ: ([js : Indexes])
(proc (vector->immutable-vector js)))))) (proc (vector->immutable-vector js))))))
(: unsafe-build-array (All (A) (Indexes (Indexes -> A) -> (Array A)))) (: build-strict-array (All (A) (In-Indexes (Indexes -> A) -> (Array A))))
(define (unsafe-build-array ds proc) (define (build-strict-array ds proc)
(Array ds 0 #f proc)) (let ([ds (check-array-shape
ds (λ () (raise-argument-error 'build-strict-array "(Vectorof Index)" 0 ds proc)))])
(unsafe-build-strict-array ds (λ: ([js : Indexes])
(proc (vector->immutable-vector js))))))
(: flat-list->array (All (A) ((Vectorof Integer) (Listof A) -> (Array A)))) (: flat-list->array (All (A) (In-Indexes (Listof A) -> (Array A))))
(define (flat-list->array ds lst) (define (flat-list->array ds lst)
(let ([ds (check-array-shape ds (λ () (raise-argument-error 'array "(Vectorof Index)" ds)))]) (let ([ds (check-array-shape ds (λ () (raise-argument-error 'array "(Vectorof Index)" ds)))])
(define vs (list->vector lst)) (define vs (list->vector lst))
(unsafe-build-array (unsafe-build-strict-array
ds (λ: ([js : Indexes]) (unsafe-vector-ref vs (unsafe-array-index->value-index ds js)))))) ds (λ: ([js : Indexes]) (unsafe-vector-ref vs (unsafe-array-index->value-index ds js))))))
(: array-lazy (All (A) ((Array A) -> (Array A)))) (: array-lazy (All (A) ((Array A) -> (Array A))))
(define (array-lazy arr) (define (array-lazy arr)
(define ds (Array-shape arr)) (define ds (Array-shape arr))
(define size (Array-size arr))
(define proc (Array-unsafe-proc arr)) (define proc (Array-unsafe-proc arr))
(define: vs : (Vectorof (Promise A)) (define: vs : (Vectorof (Promise A))
(inline-build-array-data (inline-build-array-data
ds (λ (js j) ds (λ (js j)
;; Because `delay' captures js in its closure, if we don't make this copy, `js' will have ;; Because `delay' captures js in its closure, if we don't make this copy, `js' will have
;; been mutated by the time the promise is forced ;; been mutated by the time the promise is forced
;; Using vector->immutable-vector to copy won't work: `proc' might need to mutate `js'
(let ([js (vector-copy-all js)]) (let ([js (vector-copy-all js)])
(delay (proc js)))) (delay (proc js))))
(Promise A))) (Promise A)))
(unsafe-build-array (define (strict!) (for: ([v (in-vector vs)]) (force v)))
ds (λ: ([js : Indexes]) (define unsafe-proc
(force (unsafe-vector-ref vs (unsafe-array-index->value-index ds js)))))) (λ: ([js : Indexes])
(force (unsafe-vector-ref vs (unsafe-array-index->value-index ds js)))))
(Array ds size ((inst box Boolean) #f) strict! unsafe-proc))
;; =================================================================================================== ;; ===================================================================================================
;; Abstract settable array data type ;; Abstract settable array data type

View File

@ -21,7 +21,7 @@
(define (unsafe-mutable-array ds vs) (define (unsafe-mutable-array ds vs)
(define proc (make-unsafe-array-proc ds (λ (j) (unsafe-vector-ref vs j)))) (define proc (make-unsafe-array-proc ds (λ (j) (unsafe-vector-ref vs j))))
(define set-proc (make-unsafe-array-set-proc A ds (λ (j v) (unsafe-vector-set! vs j v)))) (define set-proc (make-unsafe-array-set-proc A ds (λ (j v) (unsafe-vector-set! vs j v))))
(Mutable-Array ds (vector-length vs) #t proc set-proc vs)) (Mutable-Array ds (vector-length vs) (box #t) void proc set-proc vs))
(: make-mutable-array (All (A) (In-Indexes (Vectorof A) -> (Mutable-Array A)))) (: make-mutable-array (All (A) (In-Indexes (Vectorof A) -> (Mutable-Array A))))
(define (make-mutable-array ds vs) (define (make-mutable-array ds vs)
@ -48,8 +48,3 @@
(define ds (array-shape arr)) (define ds (array-shape arr))
(define g (unsafe-array-proc arr)) (define g (unsafe-array-proc arr))
(unsafe-mutable-array ds (inline-build-array-data ds (λ (js j) (g js)) A))) (unsafe-mutable-array ds (inline-build-array-data ds (λ (js j) (g js)) A)))
(: array-strict (All (A) ((Array A) -> (Array A))))
(define (array-strict arr)
(cond [(array-strict? arr) arr]
[else (array->mutable-array arr)]))

View File

@ -37,6 +37,12 @@
(loop (+ i 1) (* n d))] (loop (+ i 1) (* n d))]
[else n]))) [else n])))
(: check-array-shape-size (Symbol Indexes -> Index))
(define (check-array-shape-size name ds)
(define size (array-shape-size ds))
(cond [(index? size) size]
[else (error name "array size ~e (for shape ~e) is too large (is not an Index)" size ds)]))
(: check-array-shape (In-Indexes (-> Nothing) -> Indexes)) (: check-array-shape (In-Indexes (-> Nothing) -> Indexes))
(define (check-array-shape ds fail) (define (check-array-shape ds fail)
(define dims (vector-length ds)) (define dims (vector-length ds))

View File

@ -84,7 +84,7 @@
(raise-argument-error 'list*->array "rectangular (Listof* A)" lst)) (raise-argument-error 'list*->array "rectangular (Listof* A)" lst))
(define ds (list-shape lst pred?)) (define ds (list-shape lst pred?))
(cond [(pred? lst) (unsafe-build-array #() (λ (js) lst))] (cond [(pred? lst) (unsafe-build-strict-array #() (λ (js) lst))]
[ds (let ([ds (check-array-shape ds raise-shape-error)]) [ds (let ([ds (check-array-shape ds raise-shape-error)])
(define size (array-shape-size ds)) (define size (array-shape-size ds))
(unsafe-mutable-array ds (list*->flat-vector lst size pred?)))] (unsafe-mutable-array ds (list*->flat-vector lst size pred?)))]
@ -97,7 +97,7 @@
(raise-argument-error 'vector*->array "rectangular (Vectorof* A)" vec)) (raise-argument-error 'vector*->array "rectangular (Vectorof* A)" vec))
(define ds (vector-shape vec pred?)) (define ds (vector-shape vec pred?))
(cond [(pred? vec) (unsafe-build-array #() (λ (js) vec))] (cond [(pred? vec) (unsafe-build-strict-array #() (λ (js) vec))]
[ds (let ([ds (check-array-shape ds raise-shape-error)]) [ds (let ([ds (check-array-shape ds raise-shape-error)])
(define dims (vector-length ds)) (define dims (vector-length ds))
(unsafe-build-array (unsafe-build-array

View File

@ -82,14 +82,14 @@ are @deftech{non-strict}. Almost all array functions exported by @racket[math/ar
non-strict arrays. Exceptions are noted in the documentation. non-strict arrays. Exceptions are noted in the documentation.
@bold{Non-strict arrays are not lazy.} By default, arrays do not cache computed elements, but @bold{Non-strict arrays are not lazy.} By default, arrays do not cache computed elements, but
like functions, recompute them every time they are referred to. See @secref{array:strictness} like functions, recompute them every time they are referred to. Unlike functions, they can have
for details. every element computed and cached at once. See @secref{array:strictness} for details.
A @deftech{pointwise} operation is one that operates on each array element independently, on each A @deftech{pointwise} operation is one that operates on each array element independently, on each
corresponding pair of elements from two arrays independently, or on a corresponding collection corresponding pair of elements from two arrays independently, or on a corresponding collection
of elements from many arrays independently. This is usually done using @racket[array-map]. of elements from many arrays independently. This is usually done using @racket[array-map].
When a pointwise operation is performed on two arrays with different shapes, the arrays are When a pointwise operation is performed on arrays with different shapes, the arrays are
@deftech{broadcast} so that their shapes match. See @secref{array:broadcasting} for details. @deftech{broadcast} so that their shapes match. See @secref{array:broadcasting} for details.
@subsection{Quick Start} @subsection{Quick Start}
@ -113,9 +113,9 @@ Arrays can be made @tech{strict} using @racket[array-strict] or @racket[array->m
@interaction[#:eval typed-eval @interaction[#:eval typed-eval
(array-strict arr) (array-strict arr)
(array->mutable-array arr)] (array->mutable-array arr)]
The difference is that @racket[(array-strict arr)] returns @racket[arr] whenever @racket[arr] is While @racket[(array-strict arr)] causes @racket[arr] to compute and store all of its elements
already strict. (It therefore has a less precise return type.) at once, @racket[array->mutable-array] makes a strict copy. See @secref{array:strictness} for
See @secref{array:strictness} for details. details.
Arrays can be indexed using @racket[array-ref], and settable arrays can be mutated using Arrays can be indexed using @racket[array-ref], and settable arrays can be mutated using
@racket[array-set!]: @racket[array-set!]:
@ -189,17 +189,18 @@ between array operations, so not doing so provides opportunities for future para
@margin-note*{Still, it is easier to reason about non-strict array performance than lazy array @margin-note*{Still, it is easier to reason about non-strict array performance than lazy array
performance.} performance.}
The downside is that it is more difficult to reason about the performance characteristics of One downside is that it is more difficult to reason about the performance characteristics of
operations on non-strict arrays. Also, when using arrays, you must decide which arrays to make operations on non-strict arrays. Also, when using arrays, the user must decide which arrays to make
strict. (This can be done using the @racket[array-strict] function.) Fortunately, there is a strict. (This can be done using @racket[array-strict!] or @racket[array-strict].) Fortunately,
simple rule of thumb: there is a simple rule of thumb:
@nested[#:style 'inset]{@bold{Make arrays strict when you must refer to most of their elements @nested[#:style 'inset]{@bold{Make arrays strict when you must refer to most of their elements
more than once or twice.}} more than once or twice.}}
Additionally, having to name an array is a good indicator that it should be strict. In the Additionally, having to name an array is a good indicator that it should be strict. In the
following example, which computes @racket[(+ (expt x x) (expt x x))] for @racket[x] from @racket[0] following example, which computes @racket[(+ (expt x x) (expt x x))] for @racket[x] from @racket[0]
to @racket[2499], every element in @racket[xrr] is computed twice: to @racket[2499], elements in @racket[xrr] are computed twice whenever elements in @racket[res]
are referred to:
@racketblock[(define xrr (array-expt (index-array #(50 50)) @racketblock[(define xrr (array-expt (index-array #(50 50))
(index-array #(50 50)))) (index-array #(50 50))))
(define res (array+ xrr xrr))] (define res (array+ xrr xrr))]
@ -208,12 +209,9 @@ Having to name @racket[xrr] means we should make it strict:
(array-expt (index-array #(50 50)) (array-expt (index-array #(50 50))
(index-array #(50 50))))) (index-array #(50 50)))))
(define res (array+ xrr xrr))] (define res (array+ xrr xrr))]
Doing so halves the time it takes to compute @racket[res]'s elements because each Doing so halves the time it takes to compute @racket[res]'s elements.
@racket[(expt x x)] is computed only once.
An exception to these guidelines is non-strict arrays returned from constructors like An exception to these guidelines is returning an array from a function. In this
@racket[make-array], which barely compute anything at all. Such exceptions are noted in each
function's documentation. Another exception is returning an array from a function. In this
case, application sites should make the array strict, if necessary. case, application sites should make the array strict, if necessary.
If you cannot determine whether to make arrays strict, or are using arrays for so-called If you cannot determine whether to make arrays strict, or are using arrays for so-called
@ -423,9 +421,27 @@ their predicates have @racket[Struct] filters:
@defproc[(array-strict? [arr (Array A)]) Boolean]{ @defproc[(array-strict? [arr (Array A)]) Boolean]{
Returns @racket[#t] when @racket[arr] is @tech{strict}. Returns @racket[#t] when @racket[arr] is @tech{strict}.
@examples[#:eval typed-eval @examples[#:eval typed-eval
(define arr (array #[0 1 2 3])) (define arr (array+ (array 10) (array #[0 1 2 3])))
(array-strict? arr) (array-strict? arr)
(array-strict? (array-strict arr))] (array-strict! arr)
(array-strict? arr)]
}
@defproc[(array-strict! [arr (Array A)]) Void]{
Causes @racket[arr] to compute and store all of its elements. Thereafter, @racket[arr]
computes its elements by retrieving them from the store.
If @racket[arr] is already strict, @racket[(array-strict! arr)] does nothing.
}
@defform[(array-strict arr)
#:contracts ([arr (Array A)])]{
An expression form of @racket[array-strict!], which is often more convenient. First evaluates
@racket[(array-strict! arr)], then returns @racket[arr].
This is a macro so that Typed Racket will preserve @racket[arr]'s type exactly. If it were a
function, @racket[(array-strict arr)] would always have the type @racket[(Array A)], even if
@racket[arr] were a subtype of @racket[(Array A)], such as @racket[(Mutable-Array A)].
} }
@defproc[(array-shape [arr (Array A)]) Indexes]{ @defproc[(array-shape [arr (Array A)]) Indexes]{
@ -463,7 +479,7 @@ Returns the vector of data that @racket[arr] contains.
@section{Construction} @section{Construction}
@defform[(array #[#[...] ...])]{ @defform[(array #[#[...] ...])]{
Creates an @racket[Array] from nested rows of expressions. Creates a @tech{strict} @racket[Array] from nested rows of expressions.
The vector syntax @racket[#[...]] delimits rows. These may be nested to any depth, and must have a The vector syntax @racket[#[...]] delimits rows. These may be nested to any depth, and must have a
rectangular shape. Using square parentheses is not required, but is encouraged to help distinguish rectangular shape. Using square parentheses is not required, but is encouraged to help distinguish
@ -515,17 +531,17 @@ the array's elements:
@defproc[(make-array [ds In-Indexes] [value A]) (Array A)]{ @defproc[(make-array [ds In-Indexes] [value A]) (Array A)]{
Returns an array with @tech{shape} @racket[ds], with every element's value as @racket[value]. Returns an array with @tech{shape} @racket[ds], with every element's value as @racket[value].
Analogous to @racket[make-vector], but the result is @tech{non-strict}. Analogous to @racket[make-vector], but does not allocate storage for its elements.
@examples[#:eval typed-eval @examples[#:eval typed-eval
(make-array #() 5) (make-array #() 5)
(make-array #(1 2) 'sym) (make-array #(1 2) 'sym)
(make-array #(4 0 2) "Invisible")] (make-array #(4 0 2) "Invisible")]
It is useless to make a strict copy of an array returned by @racket[make-array]. The arrays returned by @racket[make-array] are @tech{strict}.
} }
@defproc[(build-array [ds In-Indexes] [proc (Indexes -> A)]) (Array A)]{ @defproc[(build-array [ds In-Indexes] [proc (Indexes -> A)]) (Array A)]{
Returns an array with @tech{shape} @racket[ds] and @tech{procedure} @racket[proc]. Returns an array with @tech{shape} @racket[ds] and @tech{procedure} @racket[proc].
Analogous to @racket[build-vector], but the result is @tech{non-strict}. Analogous to @racket[build-vector], but returns a @tech{non-strict} array.
@examples[#:eval typed-eval @examples[#:eval typed-eval
(eval:alts (eval:alts
(define: fibs : (Array Natural) (define: fibs : (Array Natural)
@ -539,23 +555,12 @@ Analogous to @racket[build-vector], but the result is @tech{non-strict}.
(eval:alts (eval:alts
fibs fibs
(ann (array #[0 1 1 2 3 5 8 13 21 34]) (Array Natural)))] (ann (array #[0 1 1 2 3 5 8 13 21 34]) (Array Natural)))]
Because @racket[build-array] returns a non-strict array, @racket[fibs] may refer to itself Because @racket[build-array] returns non-strict arrays, @racket[fibs] may refer to itself
within its definition. Of course, this naïve implementation computes its elements in time within its definition. Of course, this naïve implementation computes its elements in time
exponential in the size of @racket[fibs]. A quick, widely applicable fix is given in exponential in the size of @racket[fibs]. A quick, widely applicable fix is given in
@racket[array-lazy]'s documentation. @racket[array-lazy]'s documentation.
} }
@defproc[(array-strict [arr (Array A)]) (Array A)]{
Returns a @tech{strict} array with the same elements as @racket[arr]. If
@racket[(array-strict? arr)] is @racket[#t], returns @racket[arr].
Currently, if @racket[(array-strict? arr)] is @racket[#f], @racket[(array-strict arr)] returns
a new @racket[Mutable-Array]. Typed Racket code is unlikely to accidentally rely on this fact
(see @racket[mutable-array?] for the reason), but untyped Racket code could easily do so. Refrain.
The type used for new strict arrays could change to another descendant of @racket[Array] in a
future release. Use @racket[array->mutable-array] to reliably get a mutable array.
}
@defproc[(array-lazy [arr (Array A)]) (Array A)]{ @defproc[(array-lazy [arr (Array A)]) (Array A)]{
Returns a @tech{non-strict} array with the same elements as @racket[arr], but element Returns a @tech{non-strict} array with the same elements as @racket[arr], but element
computations are cached and reused. computations are cached and reused.
@ -577,7 +582,7 @@ time. Speeding it up to linear time only requires wrapping its definition with @
fibs fibs
(ann (array #[0 1 1 2 3 5 8 13 21 34]) (Array Natural)))] (ann (array #[0 1 1 2 3 5 8 13 21 34]) (Array Natural)))]
Printing a lazy array computes and caches all of its elements, as does applying Printing a lazy array computes and caches all of its elements, as does applying
@racket[array-strict] to it. @racket[array-strict!] to it.
} }
@defproc[(make-mutable-array [ds In-Indexes] [vs (Vectorof A)]) (Mutable-Array A)]{ @defproc[(make-mutable-array [ds In-Indexes] [vs (Vectorof A)]) (Mutable-Array A)]{
@ -591,16 +596,12 @@ Returns a mutable array with @tech{shape} @racket[ds] and elements @racket[vs];
} }
@defproc[(array->mutable-array [arr (Array A)]) (Mutable-Array A)]{ @defproc[(array->mutable-array [arr (Array A)]) (Mutable-Array A)]{
Returns a mutable array with the same elements as @racket[arr]. Returns a mutable array with the same elements as @racket[arr]. The result is a copy of
@racket[arr], even when @racket[arr] is mutable.
While @racket[array-strict] may return any subtype of @racket[Array], @racket[array->mutable-array]
always returns a @racket[Mutable-Array]. Additionally, @racket[(array-strict arr)] may return
@racket[arr] if @racket[arr] is already strict, but @racket[array->mutable-array] always makes a
copy.
} }
@defproc[(mutable-array-copy [arr (Mutable-Array A)]) (Mutable-Array A)]{ @defproc[(mutable-array-copy [arr (Mutable-Array A)]) (Mutable-Array A)]{
Like @racket[(array->mutable-array arr)], but is restricted to mutable arrays. It is also faster. Like @racket[(array->mutable-array arr)], but restricted to mutable arrays. It is also faster.
} }
@defproc[(indexes-array [ds In-Indexes]) (Array Indexes)]{ @defproc[(indexes-array [ds In-Indexes]) (Array Indexes)]{
@ -611,6 +612,8 @@ array.
(indexes-array #(4)) (indexes-array #(4))
(indexes-array #(2 3)) (indexes-array #(2 3))
(indexes-array #(4 0 2))] (indexes-array #(4 0 2))]
The resulting array does not allocate storage for its elements, and is @tech{strict}.
(It is essentially the identity function for the domain @racket[ds].)
} }
@defproc[(index-array [ds In-Indexes]) (Array Index)]{ @defproc[(index-array [ds In-Indexes]) (Array Index)]{
@ -619,6 +622,7 @@ the array.
@examples[#:eval typed-eval @examples[#:eval typed-eval
(index-array #(2 3)) (index-array #(2 3))
(array-flatten (index-array #(2 3)))] (array-flatten (index-array #(2 3)))]
Like @racket[indexes-array], this does not allocate storage for its elements, and is @tech{strict}.
} }
@defproc[(axis-index-array [ds In-Indexes] [axis Integer]) (Array Index)]{ @defproc[(axis-index-array [ds In-Indexes] [axis Integer]) (Array Index)]{
@ -629,7 +633,7 @@ Returns an array with @tech{shape} @racket[ds], with each element set to its pos
(axis-index-array #(3 3) 0) (axis-index-array #(3 3) 0)
(axis-index-array #(3 3) 1) (axis-index-array #(3 3) 1)
(axis-index-array #() 0)] (axis-index-array #() 0)]
It is useless to make a strict copy of an array returned by @racket[axis-index-array]. Like @racket[indexes-array], this does not allocate storage for its elements, and is @tech{strict}.
} }
@defproc[(diagonal-array [dims Integer] [axes-length Integer] [on-value A] [off-value A]) @defproc[(diagonal-array [dims Integer] [axes-length Integer] [on-value A] [off-value A])
@ -639,6 +643,7 @@ on the diagonal (i.e. at indexes of the form @racket[(vector j j ...)] for @rack
have the value @racket[on-value]; the rest have the value @racket[off-value]. have the value @racket[on-value]; the rest have the value @racket[off-value].
@examples[#:eval typed-eval @examples[#:eval typed-eval
(diagonal-array 2 7 1 0)] (diagonal-array 2 7 1 0)]
Like @racket[indexes-array], this does not allocate storage for its elements, and is @tech{strict}.
} }

View File

@ -9,13 +9,13 @@
(define x (list->array (sequence->list (in-range -2.0 0.800001 step)))) (define x (list->array (sequence->list (in-range -2.0 0.800001 step))))
(define y (array-slice-ref (list->array (sequence->list (in-range -1.4 1.400001 step))) (define y (array-slice-ref (list->array (sequence->list (in-range -1.4 1.400001 step)))
(list ::... (::new 1)))) (list ::... (::new 1))))
(define c (array->fcarray (array+ x (array-scale y +1.0i)))) (define c (array->fcarray (array-make-rectangular x y)))
(define-values (z divtime) (define-values (z divtime)
(for/fold: ([z : FCArray c] (for/fold: ([z : FCArray c]
[divtime : (Array Integer) (make-array (array-shape c) max-iters)] [divtime : (Array Integer) (array max-iters)]
) ([i (in-range max-iters)]) ) ([i (in-range max-iters)])
(let ([z (fcarray+ (fcarray-sqr z) c)]) (let ([z (fcarray+ (fcarray-sqr z) c)])
(values z (array-strict (array-if (array-and (array> (fcarray-magnitude z) (flarray 4.0)) (values z (array-strict (array-if (array-and (array> (fcarray-magnitude z) (array 4.0))
(array= divtime (array max-iters))) (array= divtime (array max-iters)))
(array i) (array i)
divtime)))))) divtime))))))