revised 'for' and docs
svn: r6400
This commit is contained in:
parent
f70d12b1b7
commit
c59c7ebab7
|
@ -1,33 +1,37 @@
|
|||
(module for mzscheme
|
||||
|
||||
(provide fold-for fold-for-values fold-for* fold-for*-values
|
||||
(provide for/fold for/fold-values for*/fold for*/fold-values
|
||||
for for-values for* for*-values
|
||||
list-for list-for-values list-for* list-for*-values
|
||||
lists-for lists-for-values lists-for* lists-for*-values
|
||||
and-for and-for-values and-for* and-for*-values
|
||||
or-for or-for-values or-for* or-for*-values
|
||||
first-for first-for-values first-for* first-for*-values
|
||||
last-for last-for-values last-for* last-for*-values
|
||||
for/list for/list-values for*/list for*/list-values
|
||||
for/lists for/lists-values for*/lists for*/lists-values
|
||||
for/and for/and-values for*/and for*/and-values
|
||||
for/or for/or-values for*/or for*/or-values
|
||||
for/first for/first-values for*/first for*/first-values
|
||||
for/last for/last-values for*/last for*/last-values
|
||||
|
||||
(rename *in-range in-range)
|
||||
(rename *in-nats in-nats)
|
||||
(rename *in-naturals in-naturals)
|
||||
(rename *in-list in-list)
|
||||
(rename *in-vector in-vector)
|
||||
(rename *in-string in-string)
|
||||
(rename *in-bytes in-bytes)
|
||||
in-input-port-bytes
|
||||
in-input-port-chars
|
||||
in-hash-table
|
||||
in-hash-table-keys
|
||||
in-hash-table-values
|
||||
in-hash-table-pairs
|
||||
|
||||
in-parallel
|
||||
stop-before
|
||||
stop-after
|
||||
(rename *in-indexed in-indexed)
|
||||
|
||||
sequence-run
|
||||
sequence-generator
|
||||
|
||||
define-sequence-syntax
|
||||
make-do-sequence
|
||||
:do-gen)
|
||||
:do-in)
|
||||
|
||||
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; sequence transformers:
|
||||
|
@ -44,9 +48,9 @@
|
|||
0))
|
||||
|
||||
(define (create-sequence-transformer proc1 proc2 cert)
|
||||
(unless (if (identifier? proc1)
|
||||
(unless (or (identifier? proc1)
|
||||
(and (procedure? proc1)
|
||||
(procedure-arity-includes? proc1 1))
|
||||
(procedure-arity-includes? proc1 1)))
|
||||
(raise-type-error 'define-sequence-syntax
|
||||
"identifier of procedure (arity 1)"
|
||||
0
|
||||
|
@ -67,18 +71,18 @@
|
|||
stx)))
|
||||
proc1)
|
||||
proc2
|
||||
cert)))
|
||||
cert))
|
||||
|
||||
(define (certify-clause clause certifier introducer)
|
||||
;; This is slightly painful. The painsion into `:do-gen' involves a lot of pieces
|
||||
;; This is slightly painful. The painsion into `:do-in' involves a lot of pieces
|
||||
;; that are no treated as sub-expressions. We have to push the certificates
|
||||
;; down to all the relevant identifiers and expressions:
|
||||
(define (cert s) (certifier s #f introducer))
|
||||
(define (map-cert s) (map (lambda (s) (certifier s #f #;introducer))
|
||||
(syntax->list s)))
|
||||
|
||||
(syntax-case clause (:do-gen)
|
||||
[[(id ...) (:do-gen ([(outer-id ...) outer-expr] ...)
|
||||
(syntax-case clause (:do-in)
|
||||
[[(id ...) (:do-in ([(outer-id ...) outer-expr] ...)
|
||||
outer-check
|
||||
([loop-id loop-expr] ...)
|
||||
pos-guard
|
||||
|
@ -99,7 +103,7 @@
|
|||
[pre-guard (cert #'pre-guard)]
|
||||
[post-guard (cert #'post-guard)]
|
||||
[(loop-arg ...) (map-cert #'(loop-arg ...))])
|
||||
#`[(id ...) (:do-gen ([(outer-id ...) outer-expr] ...)
|
||||
#`[(id ...) (:do-in ([(outer-id ...) outer-expr] ...)
|
||||
outer-check
|
||||
([loop-id loop-expr] ...)
|
||||
pos-guard
|
||||
|
@ -115,7 +119,7 @@
|
|||
|
||||
(define (expand-clause orig-stx clause)
|
||||
(let eloop ([use-transformer? #t])
|
||||
(syntax-case clause (values in-parallel stop-before stop-after :do-gen)
|
||||
(syntax-case clause (values in-parallel stop-before stop-after :do-in)
|
||||
[[(id ...) rhs]
|
||||
(let ([ids (syntax->list #'(id ...))])
|
||||
(for-each (lambda (id)
|
||||
|
@ -149,7 +153,7 @@
|
|||
certifier
|
||||
introducer))
|
||||
(eloop #f)))))]
|
||||
[[(id ...) (:do-gen . body)]
|
||||
[[(id ...) (:do-in . body)]
|
||||
(syntax-case #'body ()
|
||||
[(([(outer-id ...) outer-rhs] ...)
|
||||
outer-check
|
||||
|
@ -159,7 +163,7 @@
|
|||
pre-guard
|
||||
post-guard
|
||||
(loop-arg ...)) #'body]
|
||||
[else (raise-syntax-error #f "bad :do-gen clause" orig-stx clause)])]
|
||||
[else (raise-syntax-error #f "bad :do-in clause" orig-stx clause)])]
|
||||
[[(id) (values rhs)]
|
||||
(expand-clause orig-stx #'[(id) rhs])]
|
||||
[[(id ...) (in-parallel rhs ...)]
|
||||
|
@ -243,7 +247,7 @@
|
|||
orig-stx
|
||||
clause)]))))
|
||||
|
||||
(define-syntax (:do-gen stx)
|
||||
(define-syntax (:do-in stx)
|
||||
(raise-syntax-error #f "illegal outside of a loop or comprehension binding" stx))
|
||||
|
||||
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -284,7 +288,8 @@
|
|||
(vector? v)
|
||||
(string? v)
|
||||
(bytes? v)
|
||||
(input-port? v)))
|
||||
(input-port? v)
|
||||
(hash-table? v)))
|
||||
|
||||
(define (make-sequence who v)
|
||||
(cond
|
||||
|
@ -294,6 +299,7 @@
|
|||
[(string? v) (:string-gen v)]
|
||||
[(bytes? v) (:bytes-gen v)]
|
||||
[(input-port? v) (:input-port-gen v)]
|
||||
[(hash-table? v) (:hash-table-gen v cons (lambda (p) (values (car p) (cdr p))))]
|
||||
[else (raise
|
||||
(make-exn:fail:contract
|
||||
(format "for: expected a sequence for ~a, got something else: ~v"
|
||||
|
@ -322,17 +328,26 @@
|
|||
(lambda (x) #t)
|
||||
(lambda (x y) #t))))]))
|
||||
|
||||
(define (in-nats)
|
||||
(make-do-sequence (lambda ()
|
||||
(values values
|
||||
add1
|
||||
0
|
||||
(lambda (x) #t)
|
||||
(lambda (x) #t)
|
||||
(lambda (x y) #t)))))
|
||||
(define in-naturals
|
||||
(case-lambda
|
||||
[() (in-naturals 0)]
|
||||
[(n)
|
||||
(unless (and (integer? n)
|
||||
(exact? n)
|
||||
(n . >= . 0))
|
||||
(raise-type-error 'in-naturals
|
||||
"exact non-negative integer"
|
||||
n))
|
||||
(make-do-sequence (lambda ()
|
||||
(values values
|
||||
add1
|
||||
n
|
||||
(lambda (x) #t)
|
||||
(lambda (x) #t)
|
||||
(lambda (x y) #t))))]))
|
||||
|
||||
(define (in-list l)
|
||||
(unless (list? l) (raise-type-error 'in-list "list" l))
|
||||
; (unless (list? l) (raise-type-error 'in-list "list" l))
|
||||
(make-do-sequence (lambda () (:list-gen l))))
|
||||
|
||||
(define (:list-gen l)
|
||||
|
@ -402,6 +417,23 @@
|
|||
(lambda (x) (not (eof-object? x)))
|
||||
(lambda (x v) #t)))))
|
||||
|
||||
(define (in-hash-table ht)
|
||||
(unless (hash-table? ht) (raise-type-error 'in-hash-table "hash-table" ht))
|
||||
(make-do-sequence (lambda () (:hash-table-gen ht cons (lambda (p) (values (car p) (cdr p)))))))
|
||||
(define (in-hash-table-keys ht)
|
||||
(unless (hash-table? ht) (raise-type-error 'in-hash-table-keys "hash-table" ht))
|
||||
(make-do-sequence (lambda () (:hash-table-gen ht (lambda (k v) k) values))))
|
||||
(define (in-hash-table-values ht)
|
||||
(unless (hash-table? ht) (raise-type-error 'in-hash-table-values "hash-table" ht))
|
||||
(make-do-sequence (lambda () (:hash-table-gen ht (lambda (k v) v) values))))
|
||||
(define (in-hash-table-pairs ht)
|
||||
(unless (hash-table? ht) (raise-type-error 'in-hash-table-values "hash-table" ht))
|
||||
(make-do-sequence (lambda () (:hash-table-gen ht cons values))))
|
||||
|
||||
(define (:hash-table-gen ht sel f)
|
||||
(let ([l (hash-table-map ht sel)])
|
||||
(values (lambda (l) (f (car l))) cdr l pair? (lambda args #t) (lambda args #t))))
|
||||
|
||||
(define (stop-before g pred)
|
||||
(unless (sequence? g) (raise-type-error 'stop-before "sequence" g))
|
||||
(unless (and (procedure? pred)
|
||||
|
@ -464,7 +496,7 @@
|
|||
(make-do-sequence
|
||||
(lambda ()
|
||||
(let-values ([(pos->vals pos-nexts inits pos-cont?s pre-cont?s post-cont?s)
|
||||
(lists-for (p->v p-s i ps? pr? po?) ([g sequences])
|
||||
(for/lists (p->v p-s i ps? pr? po?) ([g sequences])
|
||||
(make-sequence #f g))])
|
||||
(values
|
||||
(lambda (poses) (apply values (map (lambda (pos->val pos) (pos->val pos))
|
||||
|
@ -488,9 +520,9 @@
|
|||
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; runnign sequences outside of a loop:
|
||||
|
||||
(define (sequence-run g)
|
||||
(define (sequence-generator g)
|
||||
(unless (sequence? g)
|
||||
(raise-type-error 'sequence-run "sequence" g))
|
||||
(raise-type-error 'sequence-generator "sequence" g))
|
||||
(let-values ([(pos->val pos-next init pos-cont? pre-cont? post-cont?)
|
||||
(make-sequence #f g)])
|
||||
(let ([pos init])
|
||||
|
@ -545,21 +577,21 @@
|
|||
sequence-next)))))))
|
||||
|
||||
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; core `fold-for' syntax
|
||||
;; core `for/fold' syntax
|
||||
|
||||
(define-syntax values*
|
||||
(syntax-rules ()
|
||||
[(_ x) x]
|
||||
[(_ x ...) (values x ...)]))
|
||||
|
||||
(define-syntax (fold-forX/derived stx)
|
||||
(define-syntax (for/foldX/derived stx)
|
||||
(syntax-case stx ()
|
||||
;; Done case (no more clauses, and no generated clauses to emit):
|
||||
[(_ [orig-stx multi? first-multi? nested? emit? ()] ([fold-var fold-init] ...) () expr1 expr ...)
|
||||
#`(let ([fold-var fold-init] ...) (let () expr1 expr ...))]
|
||||
;; Switch-to-emit case (no more clauses to generate):
|
||||
[(_ [orig-stx multi? first-multi? nested? #f binds] ([fold-var fold-init] ...) () . body)
|
||||
#`(fold-forX/derived [orig-stx multi? first-multi? nested? #t binds] ([fold-var fold-init] ...) () . body)]
|
||||
#`(for/foldX/derived [orig-stx multi? first-multi? nested? #t binds] ([fold-var fold-init] ...) () . body)]
|
||||
;; Emit case:
|
||||
[(_ [orig-stx multi? first-multi? nested? #t binds] ([fold-var fold-init] ...) rest . body)
|
||||
(with-syntax ([(([outer-binding ...]
|
||||
|
@ -578,7 +610,7 @@
|
|||
(let-values (inner-binding ... ...)
|
||||
(if (and pre-guard ...)
|
||||
(let-values ([(fold-var ...)
|
||||
(fold-forX/derived [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-var] ...) rest . body)])
|
||||
(for/foldX/derived [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-var] ...) rest . body)])
|
||||
(if (and post-guard ...)
|
||||
(comp-loop fold-var ... loop-arg ... ...)
|
||||
(values* fold-var ...)))
|
||||
|
@ -598,7 +630,7 @@
|
|||
;; Guard case, no pending emits:
|
||||
[(_ [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-init] ...) (#:when expr . rest) . body)
|
||||
#'(if expr
|
||||
(fold-forX/derived [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-init] ...) rest . body)
|
||||
(for/foldX/derived [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-init] ...) rest . body)
|
||||
(values* fold-init ...))]
|
||||
;; Guard case, pending emits need to be flushed first
|
||||
[(_ [orig-stx multi? first-multi? nested? #f binds] ([fold-var fold-init] ...) (#:when expr . rest) . body)
|
||||
|
@ -606,7 +638,7 @@
|
|||
;; Convert single-value form to multi-value form:
|
||||
[(_ [orig-stx #f #f nested? #f binds] fold-bind ([id rhs] . rest) . body)
|
||||
(identifier? #'id)
|
||||
#'(fold-forX/derived [orig-stx #f #t nested? #f binds] fold-bind ([(id) rhs] . rest) . body)]
|
||||
#'(for/foldX/derived [orig-stx #f #t nested? #f binds] fold-bind ([(id) rhs] . rest) . body)]
|
||||
;; If we get here in single-value mode, then it's a bad clause:
|
||||
[(_ [orig-stx #f #f nested? #f binds] fold-bind (clause . rest) . body)
|
||||
(raise-syntax-error
|
||||
|
@ -621,25 +653,25 @@
|
|||
[(_ [orig-stx . _rest] . _rest2)
|
||||
(raise-syntax-error #f "bad syntax" #'orig-stx)]))
|
||||
|
||||
(define-syntax fold-for/derived
|
||||
(define-syntax for/fold/derived
|
||||
(syntax-rules ()
|
||||
[(_ orig-stx . rest)
|
||||
(fold-forX/derived [orig-stx #f #f #f #f ()] . rest)]))
|
||||
(for/foldX/derived [orig-stx #f #f #f #f ()] . rest)]))
|
||||
|
||||
(define-syntax fold-for-values/derived
|
||||
(define-syntax for/fold-values/derived
|
||||
(syntax-rules ()
|
||||
[(_ orig-stx . rest)
|
||||
(fold-forX/derived [orig-stx #t #t #f #f ()] . rest)]))
|
||||
(for/foldX/derived [orig-stx #t #t #f #f ()] . rest)]))
|
||||
|
||||
(define-syntax fold-for*/derived
|
||||
(define-syntax for*/fold/derived
|
||||
(syntax-rules ()
|
||||
[(_ orig-stx . rest)
|
||||
(fold-forX/derived [orig-stx #f #f #t #f ()] . rest)]))
|
||||
(for/foldX/derived [orig-stx #f #f #t #f ()] . rest)]))
|
||||
|
||||
(define-syntax fold-for*-values/derived
|
||||
(define-syntax for*/fold-values/derived
|
||||
(syntax-rules ()
|
||||
[(_ orig-stx . rest)
|
||||
(fold-forX/derived [orig-stx #t #t #t #f ()] . rest)]))
|
||||
(for/foldX/derived [orig-stx #t #t #t #f ()] . rest)]))
|
||||
|
||||
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; derived `for' syntax
|
||||
|
@ -684,23 +716,23 @@
|
|||
(syntax-rules ()
|
||||
[(_ (for for-values for* for*-values) fold-bind wrap rhs-wrap combine)
|
||||
(begin
|
||||
(define-syntax-via-derived for fold-for/derived fold-bind wrap rhs-wrap combine #f)
|
||||
(define-syntax-via-derived for-values fold-for-values/derived fold-bind wrap rhs-wrap combine #t)
|
||||
(define-syntax-via-derived for* fold-for*/derived fold-bind wrap rhs-wrap combine #f)
|
||||
(define-syntax-via-derived for*-values fold-for*-values/derived fold-bind wrap rhs-wrap combine #t))]))
|
||||
(define-syntax-via-derived for for/fold/derived fold-bind wrap rhs-wrap combine #f)
|
||||
(define-syntax-via-derived for-values for/fold-values/derived fold-bind wrap rhs-wrap combine #t)
|
||||
(define-syntax-via-derived for* for*/fold/derived fold-bind wrap rhs-wrap combine #f)
|
||||
(define-syntax-via-derived for*-values for*/fold-values/derived fold-bind wrap rhs-wrap combine #t))]))
|
||||
|
||||
(define-syntax (fold-for stx)
|
||||
(define-syntax (for/fold stx)
|
||||
(syntax-case stx ()
|
||||
[(_ . rest) (quasisyntax/loc stx (fold-for/derived #,stx . rest))]))
|
||||
(define-syntax (fold-for-values stx)
|
||||
[(_ . rest) (quasisyntax/loc stx (for/fold/derived #,stx . rest))]))
|
||||
(define-syntax (for/fold-values stx)
|
||||
(syntax-case stx ()
|
||||
[(_ . rest) (quasisyntax/loc stx (fold-for-values/derived #,stx . rest))]))
|
||||
(define-syntax (fold-for* stx)
|
||||
[(_ . rest) (quasisyntax/loc stx (for/fold-values/derived #,stx . rest))]))
|
||||
(define-syntax (for*/fold stx)
|
||||
(syntax-case stx ()
|
||||
[(_ . rest) (quasisyntax/loc stx (fold-for*/derived #,stx . rest))]))
|
||||
(define-syntax (fold-for*-values stx)
|
||||
[(_ . rest) (quasisyntax/loc stx (for*/fold/derived #,stx . rest))]))
|
||||
(define-syntax (for*/fold-values stx)
|
||||
(syntax-case stx ()
|
||||
[(_ . rest) (quasisyntax/loc stx (fold-for*-values/derived #,stx . rest))]))
|
||||
[(_ . rest) (quasisyntax/loc stx (for*/fold-values/derived #,stx . rest))]))
|
||||
|
||||
(define-for-variants (for for-values for* for*-values)
|
||||
([fold-var (void)])
|
||||
|
@ -708,13 +740,13 @@
|
|||
(lambda (x) x)
|
||||
(lambda (x) `(,#'begin ,x ,#'(void))))
|
||||
|
||||
(define-for-variants (list-for list-for-values list-for* list-for*-values)
|
||||
(define-for-variants (for/list for/list-values for*/list for*/list-values)
|
||||
([fold-var null])
|
||||
(lambda (x) `(,#'reverse ,x))
|
||||
(lambda (x) x)
|
||||
(lambda (x) `(,#'cons ,x ,#'fold-var)))
|
||||
|
||||
(define-for-syntax (make-lists-for-values fold-for-id)
|
||||
(define-for-syntax (make-for/lists-values for/fold-id)
|
||||
(lambda (stx)
|
||||
(syntax-case stx ()
|
||||
[(_ (id ...) bindings expr1 expr ...)
|
||||
|
@ -727,40 +759,40 @@
|
|||
id)))
|
||||
ids)
|
||||
(with-syntax ([(id2 ...) (generate-temporaries ids)]
|
||||
[fold-for fold-for-id]
|
||||
[for/fold for/fold-id]
|
||||
[orig-stx stx])
|
||||
#'(let-values ([(id ...)
|
||||
(fold-for orig-stx ([id null] ...) bindings
|
||||
(for/fold orig-stx ([id null] ...) bindings
|
||||
(let-values ([(id2 ...) (let ()
|
||||
expr1
|
||||
expr ...)])
|
||||
(values* (cons id2 id) ...)))])
|
||||
(values* (reverse id) ...))))])))
|
||||
|
||||
(define-syntax lists-for (make-lists-for-values #'fold-for/derived))
|
||||
(define-syntax lists-for-values (make-lists-for-values #'fold-for-values/derived))
|
||||
(define-syntax lists-for* (make-lists-for-values #'fold-for*/derived))
|
||||
(define-syntax lists-for*-values (make-lists-for-values #'fold-for*-values/derived))
|
||||
(define-syntax for/lists (make-for/lists-values #'for/fold/derived))
|
||||
(define-syntax for/lists-values (make-for/lists-values #'for/fold-values/derived))
|
||||
(define-syntax for*/lists (make-for/lists-values #'for*/fold/derived))
|
||||
(define-syntax for*/lists-values (make-for/lists-values #'for*/fold-values/derived))
|
||||
|
||||
(define-for-variants (and-for and-for-values and-for* and-for*-values)
|
||||
(define-for-variants (for/and for/and-values for*/and for*/and-values)
|
||||
([result #t])
|
||||
(lambda (x) x)
|
||||
(lambda (rhs) #`(stop-after #,rhs (lambda x (not result))))
|
||||
(lambda (x) x))
|
||||
|
||||
(define-for-variants (or-for or-for-values or-for* or-for*-values)
|
||||
(define-for-variants (for/or for/or-values for*/or for*/or-values)
|
||||
([result #f])
|
||||
(lambda (x) x)
|
||||
(lambda (rhs) #`(stop-after #,rhs (lambda x result)))
|
||||
(lambda (x) x))
|
||||
|
||||
(define-for-variants (first-for first-for-values first-for* first-for*-values)
|
||||
(define-for-variants (for/first for/first-values for*/first for*/first-values)
|
||||
([val #f][stop? #f])
|
||||
(lambda (x) #`(let-values ([(val _) #,x]) val))
|
||||
(lambda (rhs) #`(stop-after #,rhs (lambda x stop?)))
|
||||
(lambda (x) #`(values #,x #t)))
|
||||
|
||||
(define-for-variants (last-for last-for-values last-for* last-for*-values)
|
||||
(define-for-variants (for/last for/last-values for*/last for*/last-values)
|
||||
([result #f])
|
||||
(lambda (x) x)
|
||||
(lambda (rhs) rhs)
|
||||
|
@ -775,7 +807,7 @@
|
|||
(let loop ([stx stx])
|
||||
(syntax-case stx ()
|
||||
[[(id) (_ a b step)] #`[(id)
|
||||
(:do-gen
|
||||
(:do-in
|
||||
;; outer bindings:
|
||||
([(start) a] [(end) b] [(inc) step])
|
||||
;; outer check:
|
||||
|
@ -804,29 +836,36 @@
|
|||
[[(id) (_ b)] (loop #'[(id) (_ 0 b 1)])]
|
||||
[_else #f]))))
|
||||
|
||||
(define-sequence-syntax *in-nats
|
||||
#'in-nats
|
||||
(define-sequence-syntax *in-naturals
|
||||
#'in-naturals
|
||||
(lambda (orig-stx stx)
|
||||
(syntax-case stx ()
|
||||
[[(id) (_)] #`[(id)
|
||||
(:do-gen
|
||||
;; outer bindings:
|
||||
()
|
||||
;; outer check:
|
||||
(void)
|
||||
;; loop bindings:
|
||||
([pos 0])
|
||||
;; pos check
|
||||
#t
|
||||
;; inner bindings
|
||||
([(id) pos])
|
||||
;; pre guard
|
||||
#t
|
||||
;; post guard
|
||||
#t
|
||||
;; loop args
|
||||
((+ pos 1)))]]
|
||||
[_else #f])))
|
||||
(let loop ([stx stx])
|
||||
(syntax-case stx ()
|
||||
[[(id) (_ start)]
|
||||
(and (integer? (syntax-e #'start))
|
||||
(exact? (syntax-e #'start))
|
||||
((syntax-e #'start) . >= . 0))
|
||||
#`[(id)
|
||||
(:do-in
|
||||
;; outer bindings:
|
||||
()
|
||||
;; outer check:
|
||||
(void)
|
||||
;; loop bindings:
|
||||
([pos start])
|
||||
;; pos check
|
||||
#t
|
||||
;; inner bindings
|
||||
([(id) pos])
|
||||
;; pre guard
|
||||
#t
|
||||
;; post guard
|
||||
#t
|
||||
;; loop args
|
||||
((+ pos 1)))]]
|
||||
[[(id) (_)]
|
||||
(loop #'[(id) (_ 0)])]
|
||||
[_else #f]))))
|
||||
|
||||
(define-sequence-syntax *in-list
|
||||
#'in-list
|
||||
|
@ -834,15 +873,15 @@
|
|||
(syntax-case stx ()
|
||||
[((id) (_ lst-expr))
|
||||
#'[(id)
|
||||
(:do-gen
|
||||
(:do-in
|
||||
;;outer bindings
|
||||
([(lst) lst-expr])
|
||||
;; outer check
|
||||
(unless (list? lst) (in-list lst))
|
||||
(void) ; (unless (list? lst) (in-list lst))
|
||||
;; loop bindings
|
||||
([lst lst])
|
||||
;; pos check
|
||||
(pair? lst)
|
||||
(not (null? lst))
|
||||
;; inner bindings
|
||||
([(id) (car lst)])
|
||||
;; pre guard
|
||||
|
@ -865,7 +904,7 @@
|
|||
(syntax-case stx ()
|
||||
[((id) (_ vec-expr))
|
||||
#'[(id)
|
||||
(:do-gen
|
||||
(:do-in
|
||||
;;outer bindings
|
||||
([(vec len) (let ([vec vec-expr])
|
||||
(unless (vector? vec)
|
||||
|
@ -913,4 +952,4 @@
|
|||
(lambda (orig-stx stx)
|
||||
(syntax-case stx ()
|
||||
[((id1 id2) (_ gen-expr))
|
||||
#'[(id1 id2) (in-parallel gen-expr (*in-nats))]]))))
|
||||
#'[(id1 id2) (in-parallel gen-expr (*in-naturals))]]))))
|
||||
|
|
|
@ -46,16 +46,39 @@
|
|||
(if (flow? p)
|
||||
p
|
||||
(make-flow (list p))))))
|
||||
(append
|
||||
(if (string? (car val-list+outputs))
|
||||
(map
|
||||
(lambda (s)
|
||||
(list (make-flow (list (make-paragraph
|
||||
(list
|
||||
(hspace 2)
|
||||
(span-class "schemeerror"
|
||||
(italic s))))))))
|
||||
(let sloop ([s (car val-list+outputs)])
|
||||
(if (string=? "" (cdar val-list+outputs))
|
||||
null
|
||||
(list
|
||||
(list
|
||||
(make-flow
|
||||
(list
|
||||
(let ([s (regexp-split #rx"\n"
|
||||
(regexp-replace #rx"\n$"
|
||||
(cdar val-list+outputs)
|
||||
""))])
|
||||
(if (= 1 (length s))
|
||||
(make-paragraph
|
||||
(list
|
||||
(hspace 2)
|
||||
(span-class "schemestdout" (car s))))
|
||||
(make-table
|
||||
#f
|
||||
(map (lambda (s)
|
||||
(list (make-flow (list (make-paragraph
|
||||
(list
|
||||
(hspace 2)
|
||||
(span-class "schemestdout" s)))))))
|
||||
s)))))))))
|
||||
(if (string? (caar val-list+outputs))
|
||||
;; Error result case:
|
||||
(map
|
||||
(lambda (s)
|
||||
(list (make-flow (list (make-paragraph
|
||||
(list
|
||||
(hspace 2)
|
||||
(span-class "schemeerror"
|
||||
(italic s))))))))
|
||||
(let sloop ([s (caar val-list+outputs)])
|
||||
(if ((string-length s) . > . maxlen)
|
||||
;; break the error message into multiple lines:
|
||||
(let loop ([pos (sub1 maxlen)])
|
||||
|
@ -67,43 +90,20 @@
|
|||
(sloop (substring s (add1 pos))))]
|
||||
[else (loop (sub1 pos))]))
|
||||
(list s))))
|
||||
(append
|
||||
(if (string=? "" (cdar val-list+outputs))
|
||||
;; Normal result case:
|
||||
(let ([val-list (caar val-list+outputs)])
|
||||
(if (equal? val-list (list (void)))
|
||||
null
|
||||
(list
|
||||
(list
|
||||
(make-flow
|
||||
(list
|
||||
(let ([s (regexp-split #rx"\n"
|
||||
(regexp-replace #rx"\n$"
|
||||
(cdar val-list+outputs)
|
||||
""))])
|
||||
(if (= 1 (length s))
|
||||
(make-paragraph
|
||||
(list
|
||||
(hspace 2)
|
||||
(span-class "schemestdout" (car s))))
|
||||
(make-table
|
||||
#f
|
||||
(map (lambda (s)
|
||||
(list (make-flow (list (make-paragraph
|
||||
(list
|
||||
(hspace 2)
|
||||
(span-class "schemestdout" s)))))))
|
||||
s)))))))))
|
||||
(let ([val-list (caar val-list+outputs)])
|
||||
(if (equal? val-list (list (void)))
|
||||
null
|
||||
(map (lambda (v)
|
||||
(list (make-flow (list (make-paragraph
|
||||
(list
|
||||
(hspace 2)
|
||||
(span-class "schemeresult"
|
||||
(to-element/no-color v))))))))
|
||||
val-list)))))
|
||||
(loop (cdr expr-paras)
|
||||
(cdr val-list+outputs)
|
||||
#f))))))))
|
||||
(map (lambda (v)
|
||||
(list (make-flow (list (make-paragraph
|
||||
(list
|
||||
(hspace 2)
|
||||
(span-class "schemeresult"
|
||||
(to-element/no-color v))))))))
|
||||
val-list))))
|
||||
(loop (cdr expr-paras)
|
||||
(cdr val-list+outputs)
|
||||
#f)))))))
|
||||
|
||||
(define (do-eval s)
|
||||
(cond
|
||||
|
@ -121,7 +121,8 @@
|
|||
(let ([o (open-output-string)])
|
||||
(parameterize ([current-output-port o])
|
||||
(with-handlers ([exn? (lambda (e)
|
||||
(exn-message e))])
|
||||
(cons (exn-message e)
|
||||
(get-output-string o)))])
|
||||
(cons (let ([v (do-plain-eval s #t)])
|
||||
(copy-value v (make-hash-table)))
|
||||
(get-output-string o)))))]))
|
||||
|
|
|
@ -43,10 +43,16 @@
|
|||
(define (to-element/id s)
|
||||
(make-element "schemesymbol" (list (to-element/no-color s))))
|
||||
|
||||
(define-code scheme to-element unsyntax (lambda (ctx s v) s))
|
||||
(define-code schemeresult to-element/result unsyntax (lambda (ctx s v) s))
|
||||
(define-code schemeid to-element/id unsyntax (lambda (ctx s v) s))
|
||||
(define-code schememodname to-element unsyntax (lambda (ctx s v) s))
|
||||
(define (keep-s-expr ctx s v) s)
|
||||
(define (add-sq-prop s name val)
|
||||
(if (eq? name 'paren-shape)
|
||||
(make-shaped-parens s val)
|
||||
s))
|
||||
|
||||
(define-code scheme to-element unsyntax keep-s-expr add-sq-prop)
|
||||
(define-code schemeresult to-element/result unsyntax keep-s-expr add-sq-prop)
|
||||
(define-code schemeid to-element/id unsyntax keep-s-expr add-sq-prop)
|
||||
(define-code schememodname to-element unsyntax keep-s-expr add-sq-prop)
|
||||
|
||||
(define (litchar . strs)
|
||||
(unless (andmap string? strs)
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
syntax-ize
|
||||
syntax-ize-hook
|
||||
current-keyword-list
|
||||
current-variable-list)
|
||||
current-variable-list
|
||||
|
||||
(struct shaped-parens (val shape)))
|
||||
|
||||
(define no-color "schemeplain")
|
||||
(define meta-color "schemeplain")
|
||||
|
@ -26,12 +28,13 @@
|
|||
(define opt-color "schemeopt")
|
||||
|
||||
(define current-keyword-list
|
||||
(make-parameter '(define let let* letrec require provide
|
||||
(make-parameter '(define let let* letrec require provide let-values
|
||||
lambda new send if cond begin else and or
|
||||
define-syntax syntax-rules define-struct
|
||||
quote quasiquote unquote unquote-splicing
|
||||
syntax quasisyntax unsyntax unsyntax-splicing
|
||||
fold-for list-for list-for* for)))
|
||||
for/fold for/list for*/list for for/and for/or for* for*/or for*/and for*/fold
|
||||
for-values for*/list-values for/first for/last)))
|
||||
(define current-variable-list
|
||||
(make-parameter null))
|
||||
|
||||
|
@ -301,6 +304,7 @@
|
|||
(string? (syntax-e c))
|
||||
(bytes? (syntax-e c))
|
||||
(char? (syntax-e c))
|
||||
(keyword? (syntax-e c))
|
||||
(boolean? (syntax-e c)))
|
||||
value-color]
|
||||
[(identifier? c)
|
||||
|
@ -336,13 +340,13 @@
|
|||
|
||||
(define-syntax (define-code stx)
|
||||
(syntax-case stx ()
|
||||
[(_ code typeset-code uncode d->s)
|
||||
[(_ code typeset-code uncode d->s stx-prop)
|
||||
(syntax/loc stx
|
||||
(define-syntax (code stx)
|
||||
(define (stx->loc-s-expr v)
|
||||
(cond
|
||||
[(syntax? v)
|
||||
(let ([mk `(d->s
|
||||
(let ([mk `(,#'d->s
|
||||
#f
|
||||
,(syntax-case v (uncode)
|
||||
[(uncode e) #'e]
|
||||
|
@ -354,7 +358,7 @@
|
|||
,(syntax-span v)))])
|
||||
(let ([prop (syntax-property v 'paren-shape)])
|
||||
(if prop
|
||||
`(syntax-property ,mk 'paren-shape ,prop)
|
||||
`(,#'stx-prop ,mk 'paren-shape ,prop)
|
||||
mk)))]
|
||||
[(pair? v) `(cons ,(stx->loc-s-expr (car v))
|
||||
,(stx->loc-s-expr (cdr v)))]
|
||||
|
@ -365,13 +369,13 @@
|
|||
[(null? v) 'null]
|
||||
[else `(quote ,v)]))
|
||||
(define (cvt s)
|
||||
(d->s #'here (stx->loc-s-expr s) #f))
|
||||
(datum->syntax-object #'here (stx->loc-s-expr s) #f))
|
||||
(syntax-case stx ()
|
||||
[(_ expr) #`(typeset-code #,(cvt #'expr))]
|
||||
[(_ expr (... ...))
|
||||
#`(typeset-code #,(cvt #'(code:line expr (... ...))))])))]
|
||||
[(_ code typeset-code uncode)
|
||||
#'(define-code code typeset-code uncode datum->syntax-object)]
|
||||
#'(define-code code typeset-code uncode datum->syntax-object syntax-property)]
|
||||
[(_ code typeset-code) #'(define-code code typeset-code unsyntax)]))
|
||||
|
||||
|
||||
|
@ -406,10 +410,16 @@
|
|||
(loop (cons (car r) r) (sub1 i)))))
|
||||
l))))
|
||||
|
||||
(define-struct shaped-parens (val shape))
|
||||
|
||||
(define (syntax-ize v col)
|
||||
(cond
|
||||
[((syntax-ize-hook) v col)
|
||||
=> (lambda (r) r)]
|
||||
[(shaped-parens? v)
|
||||
(syntax-property (syntax-ize (shaped-parens-val v) col)
|
||||
'paren-shape
|
||||
(shaped-parens-shape v))]
|
||||
[(and (list? v)
|
||||
(pair? v)
|
||||
(memq (car v) '(quote unquote unquote-splicing)))
|
||||
|
|
|
@ -20,7 +20,8 @@ built-in datatypes for simple forms of data.
|
|||
@include-section["symbols.scrbl"]
|
||||
@include-section["pairs.scrbl"]
|
||||
@include-section["vectors.scrbl"]
|
||||
@include-section["hash-tables.scrbl"]
|
||||
@include-section["keywords.scrbl"]
|
||||
@include-section["boxes.scrbl"]
|
||||
@include-section["hash-tables.scrbl"]
|
||||
@include-section["paths.scrbl"]
|
||||
@include-section["keywords.scrbl"]
|
||||
@include-section["void-and-undef.scrbl"]
|
||||
|
|
427
collects/scribblings/guide/for.scrbl
Normal file
427
collects/scribblings/guide/for.scrbl
Normal file
|
@ -0,0 +1,427 @@
|
|||
#reader(lib "docreader.ss" "scribble")
|
||||
@require[(lib "manual.ss" "scribble")]
|
||||
@require[(lib "eval.ss" "scribble")]
|
||||
@require["guide-utils.ss"]
|
||||
|
||||
@interaction-eval[(require (lib "for.ss"))]
|
||||
|
||||
@title[#:tag "for"]{Iterations and Comprehensions}
|
||||
|
||||
The @scheme[for] family of syntactic forms support iteration over
|
||||
@defterm{sequences}. Lists, vectors, strings, byte strings, input
|
||||
ports, and hash tables can all be used as sequences, and constructors
|
||||
like @scheme[in-range] offer even more kinds of sequences.
|
||||
|
||||
Variants of @scheme[for] accumulate iteration results in different
|
||||
ways, but they all have the same syntactic shape. Simplifying, for
|
||||
now, the syntax of @scheme[for] is
|
||||
|
||||
@schemeblock[
|
||||
(for ([_id _sequence-expr])
|
||||
_body-expr)
|
||||
]
|
||||
|
||||
A @scheme[for] loop iterates through the sequence produced by the
|
||||
@scheme[_sequence-expr]. For each element of the sequence,
|
||||
@scheme[for] binds the element to @scheme[_id], and then it evaluates
|
||||
the @scheme[_body-expr] for side effects.
|
||||
|
||||
@examples[
|
||||
(for ([i '(1 2 3)])
|
||||
(display i))
|
||||
(for ([i "abc"])
|
||||
(printf "~a..." i))
|
||||
]
|
||||
|
||||
The @scheme[for/list] variant of @scheme[for] is more Scheme-like. It
|
||||
accumulates @scheme[_body-expr] results into a list, instead of
|
||||
evaluating @scheme[_body-expr] only for side effects. In more
|
||||
technical terms, @scheme[for/list] implements a @defterm{list
|
||||
comprehension}.
|
||||
|
||||
@examples[
|
||||
(for/list ([i '(1 2 3)])
|
||||
(* i i))
|
||||
(for/list ([i "abc"])
|
||||
i)
|
||||
]
|
||||
|
||||
The full syntax of @scheme[for] accomodates multiple sequences to
|
||||
iterate in parallel, and the @scheme[for*] variant nests the
|
||||
iterations instead of running them in parallel. More variants of
|
||||
@scheme[for] and @scheme[for*] accumulate @scheme[_body-expr] results
|
||||
in different ways. In all of these variants, predicates that prune
|
||||
iterations can be included along with bindings.
|
||||
|
||||
Before details on the variations of @scheme[for], though, it's best to
|
||||
see the kinds of sequence generators that make interesting examples.
|
||||
|
||||
@section{Sequence Constructors}
|
||||
|
||||
The @scheme[in-range] procedure generates a sequence of numbers, given
|
||||
an optional starting number (which defaults to @scheme[0]), a number
|
||||
before which the sequences ends, and an optional step (which defaults
|
||||
to @scheme[1]).
|
||||
|
||||
@examples[
|
||||
(for ([i (in-range 3)])
|
||||
(display i))
|
||||
(for ([i (in-range 1 4)])
|
||||
(display i))
|
||||
(for ([i (in-range 1 4 2)])
|
||||
(display i))
|
||||
(for ([i (in-range 4 1 -1)])
|
||||
(display i))
|
||||
(for ([i (in-range 1 4 1/2)])
|
||||
(printf " ~a " i))
|
||||
]
|
||||
|
||||
The @scheme[in-naturals] procedure is similar, except that the
|
||||
starting number must be an exact non-negative integer (which defaults
|
||||
to @scheme[0]), the step is always @scheme[1], and there is no upper
|
||||
limit. A @scheme[for] loop using just @scheme[in-naturals] will never
|
||||
terminate.
|
||||
|
||||
@examples[
|
||||
(for ([i (in-naturals)])
|
||||
(if (= i 10)
|
||||
(error "too much!")
|
||||
(display i)))
|
||||
]
|
||||
|
||||
The @scheme[stop-before] and @scheme[stop-after] procedures construct
|
||||
a new sequence given a sequence and a predicate. The new sequence is
|
||||
like the given sequence, but truncated either immediately before or
|
||||
immediately after the first element for which the predicate returns
|
||||
true.
|
||||
|
||||
@examples[
|
||||
(for ([i (stop-before "abc def"
|
||||
char-whitespace?)])
|
||||
(display i))
|
||||
]
|
||||
|
||||
Sequence constructors like @scheme[in-list], @scheme[in-vector] and
|
||||
@scheme[in-string] simply make explicit the use of a list, vector, or
|
||||
string as a sequence. Since they raise an exception when given the
|
||||
wrong kind of value and otherwise avoid a run-time dispatch to
|
||||
determine the sequence type, they enable more efficient code
|
||||
generation; see @secref["for-performance"] for more information.
|
||||
|
||||
@examples[
|
||||
(for ([i (in-string "abc")])
|
||||
(display i))
|
||||
(for ([i (in-string '(1 2 3))])
|
||||
(display i))
|
||||
]
|
||||
|
||||
|
||||
@section{@scheme[for] and @scheme[for*]}
|
||||
|
||||
The full syntax of @scheme[for] is
|
||||
|
||||
@schemeblock[
|
||||
(for (_clause ...)
|
||||
_body-expr ...+)
|
||||
code:blank
|
||||
#, @elem{where} _clause #, @elem{is one of}
|
||||
[_id _sequence-expr]
|
||||
#:when _boolean-expr
|
||||
]
|
||||
|
||||
When multiple @scheme[[_id _sequence-expr]] clauses are provided
|
||||
in a @scheme[for] form, the corresponding sequences are traversed in
|
||||
parallel:
|
||||
|
||||
@interaction[
|
||||
(for ([i (in-range 1 4)]
|
||||
[chapter '("Intro" "Details" "Conclusion")])
|
||||
(printf "Chapter ~a. ~a\n" i chapter))
|
||||
]
|
||||
|
||||
With parallel sequences, the @scheme[for] expression stops iterating
|
||||
when any sequence ends. This behavior allows @scheme[in-naturals],
|
||||
which creates an infinite sequence of numbers, to be used for
|
||||
indexing:
|
||||
|
||||
@interaction[
|
||||
(for ([i (in-naturals 1)]
|
||||
[chapter '("Intro" "Details" "Conclusion")])
|
||||
(printf "Chapter ~a. ~a\n" i chapter))
|
||||
]
|
||||
|
||||
The @scheme[for*] form, which has the same syntax as @scheme[for],
|
||||
nests multiple sequences instead of running them in parallel:
|
||||
|
||||
@interaction[
|
||||
(for* ([book '("Guide" "Reference")]
|
||||
[chapter '("Intro" "Details" "Conclusion")])
|
||||
(printf "~a ~a\n" book chapter))
|
||||
]
|
||||
|
||||
Thus, @scheme[for*] is a shorthand for nested @scheme[for]s in the
|
||||
same way that @scheme[let*] is a shorthand for nested @scheme[let]s.
|
||||
|
||||
The @scheme[#:when _boolean-expr] form of a @scheme[_clause] is
|
||||
another shorthand. It allows the @scheme[_body-expr]s to evaluate only
|
||||
when the @scheme[_boolean-expr] produces a true value:
|
||||
|
||||
@interaction[
|
||||
(for* ([book '("Guide" "Reference")]
|
||||
[chapter '("Intro" "Details" "Conclusion")]
|
||||
#:when (not (equal? chapter "Details")))
|
||||
(printf "~a ~a\n" book chapter))
|
||||
]
|
||||
|
||||
A @scheme[_boolean-expr] with @scheme[#:when] can refer to any of the
|
||||
preceding iteration bindings. In a @scheme[for] form, this scoping
|
||||
makes sense only if the test is nested in the interation of the
|
||||
preceding bindings; thus, bindings separated by @scheme[#:when] are
|
||||
mutually nested, instead of in parallel, even with @scheme[for].
|
||||
|
||||
@interaction[
|
||||
(for ([book '("Guide" "Reference" "Notes")]
|
||||
#:when (not (equal? book "Notes"))
|
||||
[i (in-naturals 1)]
|
||||
[chapter '("Intro" "Details" "Conclusion" "Index")]
|
||||
#:when (not (equal? chapter "Index")))
|
||||
(printf "~a Chapter ~a. ~a\n" book i chapter))
|
||||
]
|
||||
|
||||
@section{@scheme[for/list] and @scheme[for*/list]}
|
||||
|
||||
The @scheme[for/list] form, which has the same syntax as @scheme[for],
|
||||
evaluates the @scheme[_body-expr]s to obtain values that go into a
|
||||
newly constructed list:
|
||||
|
||||
@interaction[
|
||||
(for/list ([i (in-naturals 1)]
|
||||
[chapter '("Intro" "Details" "Conclusion")])
|
||||
(string-append (number->string i) ". " chapter))
|
||||
]
|
||||
|
||||
A @scheme[#:when] clause in a @scheme[for-list] form prunes the result
|
||||
list along with evaluations of the @scheme[_body-expr]s:
|
||||
|
||||
@interaction[
|
||||
(for/list ([i (in-naturals 1)]
|
||||
[chapter '("Intro" "Details" "Conclusion")]
|
||||
#:when (odd? i))
|
||||
chapter)
|
||||
]
|
||||
|
||||
This pruning behavior of @scheme[#:when] is more useful with
|
||||
@scheme[for/list] than @scheme[for]. Whereas a plain @scheme[when]
|
||||
form normally suffices with @scheme[for], a @scheme[when] expression
|
||||
form in a @scheme[for/list] would cause the result list to contain
|
||||
@void-const[]s instead of omitting list elements.
|
||||
|
||||
The @scheme[for*/list] is like @scheme[for*], nesting multiple
|
||||
iterations:
|
||||
|
||||
@interaction[
|
||||
(for*/list ([book '("Guide" "Ref.")]
|
||||
[chapter '("Intro" "Details")])
|
||||
(string-append book " " chapter))
|
||||
]
|
||||
|
||||
A @scheme[for*/list] form is not quite the same thing as nested
|
||||
@scheme[for/list] forms. Nested @scheme[for/list]s would produce a
|
||||
list of lists, instead of one flattened list. Much like
|
||||
@scheme[#:when], then, the nesting of @scheme[for*/list] is more
|
||||
useful than the nesting of @scheme[for*].
|
||||
|
||||
@section{@scheme[for/and] and @scheme[for/or]}
|
||||
|
||||
The @scheme[for/and] form combines iteration results with
|
||||
@scheme[and], stopping as soon as it encounters @scheme[#f]:
|
||||
|
||||
@interaction[
|
||||
(for/and ([chapter '("Intro" "Details" "Conclusion")])
|
||||
(equal? chapter "Intro"))
|
||||
]
|
||||
|
||||
The @scheme[for/or] form combines iteration results with @scheme[or],
|
||||
stopping as soon as it encounters a true value:
|
||||
|
||||
@interaction[
|
||||
(for/or ([chapter '("Intro" "Details" "Conclusion")])
|
||||
(equal? chapter "Intro"))
|
||||
]
|
||||
|
||||
As usual, the @scheme[for*/and] and @scheme[for*/or] forms provide the
|
||||
same facility with nested iterations.
|
||||
|
||||
@section{@scheme[for/first] and @scheme[for/last]}
|
||||
|
||||
The @scheme[for/first] form returns the result of the first time that
|
||||
the @scheme[_body-expr]s are evaluated, skipping further iterations.
|
||||
This form is most useful with a @scheme[#:when] clause.
|
||||
|
||||
@interaction[
|
||||
(for/first ([chapter '("Intro" "Details" "Conclusion" "Index")]
|
||||
#:when (not (equal? chapter "Intro")))
|
||||
chapter)
|
||||
]
|
||||
|
||||
If the @scheme[_body-expr]s are evaluated zero time, then the result
|
||||
is @scheme[#f].
|
||||
|
||||
The @scheme[for/last] form runs all iterations, returning the value of
|
||||
the last iteration (or @scheme[#f] if no iterations are run):
|
||||
|
||||
@interaction[
|
||||
(for/last ([chapter '("Intro" "Details" "Conclusion" "Index")]
|
||||
#:when (not (equal? chapter "Index")))
|
||||
chapter)
|
||||
]
|
||||
|
||||
As usual, the @scheme[for*/first] and @scheme[for*/last] forms provide
|
||||
the same facility with nested iterations:
|
||||
|
||||
@interaction[
|
||||
(for*/first ([book '("Guide" "Reference")]
|
||||
[chapter '("Intro" "Details" "Conclusion" "Index")]
|
||||
#:when (not (equal? chapter "Intro")))
|
||||
(list book chapter))
|
||||
|
||||
(for*/last ([book '("Guide" "Reference")]
|
||||
[chapter '("Intro" "Details" "Conclusion" "Index")]
|
||||
#:when (not (equal? chapter "Index")))
|
||||
(list book chapter))
|
||||
]
|
||||
|
||||
@section{@scheme[for/fold] and @scheme[for*/fold]}
|
||||
|
||||
The @scheme[for/fold] form generalizes the way to combine iteration
|
||||
results. Its syntax is slightly different than the syntax of
|
||||
@scheme[for], because accumulation variables must be declared at the
|
||||
beginning:
|
||||
|
||||
@schemeblock[
|
||||
(for/fold ([_accum-id _init-expr] ...)
|
||||
(_clause ...)
|
||||
_body-expr ...+)
|
||||
]
|
||||
|
||||
In the simple case, only one @scheme[[_accum-id _init-expr]] is
|
||||
provided, and the result of the @scheme[for/fold] is the final value
|
||||
for @scheme[_accum-id], which starts out with the value of
|
||||
@scheme[_init-expr]. In the @scheme[_clause]s and
|
||||
@scheme[_body-expr]s, @scheme[_accum-id] can be referenced to gets its
|
||||
current value, and the last @scheme[_body-expr] provides the value of
|
||||
@scheme[_accum-id] for the netx iteration.
|
||||
|
||||
@examples[
|
||||
(for/fold ([len 0])
|
||||
([chapter '("Intro" "Conclusion")])
|
||||
(+ len (string-length chapter)))
|
||||
(for/fold ([prev #f])
|
||||
([i (in-naturals 1)]
|
||||
[chapter '("Intro" "Details" "Details" "Conclusion")]
|
||||
#:when (not (equal? chapter prev)))
|
||||
(printf "~a. ~a\n" i chapter)
|
||||
chapter)
|
||||
]
|
||||
|
||||
When multiple @scheme[_accum-id]s are specified, then the last
|
||||
@scheme[_body-expr] must produce multiple values, one for each
|
||||
@scheme[_accum-id]. The @scheme[for/fold] expression itself produces
|
||||
multiple values for the results.
|
||||
|
||||
@examples[
|
||||
(for/fold ([prev #f]
|
||||
[counter 1])
|
||||
([chapter '("Intro" "Details" "Details" "Conclusion")]
|
||||
#:when (not (equal? chapter prev)))
|
||||
(printf "~a. ~a\n" counter chapter)
|
||||
(values chapter
|
||||
(add1 counter)))
|
||||
]
|
||||
|
||||
@section{Multiple-Valued Sequences}
|
||||
|
||||
In the same way that a procedure or expression can produce multiple
|
||||
values, individual iterations of a sequence can produce multiple
|
||||
elements. For example, a hash table as a sequence generates two
|
||||
values for each iteration: a key and a value.
|
||||
|
||||
In the same way that @scheme[let-values] binds multiple results to
|
||||
multiple identifiers, @scheme[for-values] binds multiple sequence
|
||||
elements to multiple iteration identifiers:
|
||||
|
||||
@interaction[
|
||||
(for-values ([(k v) #hash(("apple" . 1) ("banana" . 3))])
|
||||
(printf "~a count: ~a\n" k v))
|
||||
]
|
||||
|
||||
A @schemekeywordfont{-values} variant exists for all @scheme[for]
|
||||
variants. For example, @scheme[for*/list-values] nests iterations,
|
||||
builds a list, and works with multiple-valued sequences:
|
||||
|
||||
@interaction[
|
||||
(for*/list-values ([(k v) #hash(("apple" . 1) ("banana" . 3))]
|
||||
[(i) (in-range v)])
|
||||
k)
|
||||
]
|
||||
|
||||
|
||||
@section[#:tag "for-performance"]{Iteration Performance}
|
||||
|
||||
Ideally, a @scheme[for] iteration should run as fast as a loop that
|
||||
your write by hand as a recursive-function invocation. A hand-written
|
||||
loop, however, is normally specific to a particular kind of data, such
|
||||
as lists. In that case, the hand-written loop uses @scheme[car] and
|
||||
@scheme[cdr] directly, instead of handling all forms of sequences and
|
||||
dispatching to an appropriate iterator.
|
||||
|
||||
The @scheme[for] forms can provide the performance of hand-written
|
||||
loops when enough information is apparent about the sequences to
|
||||
iterate. Specifically, the clause should have one of the following
|
||||
@scheme[_fast-clause] forms:
|
||||
|
||||
@schemeblock[
|
||||
#, @elem{a} _fast-clause #, @elem{is one of}
|
||||
[_id (in-range _expr)]
|
||||
[_id (in-range _expr _expr)]
|
||||
[_id (in-range _expr _expr _expr)]
|
||||
[_id (in-naturals)]
|
||||
[_id (in-naturals _expr)]
|
||||
[_id (in-list _expr)]
|
||||
[_id (in-vector _expr)]
|
||||
[_id (in-string _expr)]
|
||||
[_id (in-bytes _expr)]
|
||||
[_id (stop-before _fast-clause _predicate-expr)]
|
||||
[_id (stop-after _fast-clause _predicate-expr)]
|
||||
]
|
||||
|
||||
@examples[
|
||||
(time (for ([i (in-range 100000)])
|
||||
(for ([elem (in-list '(a b c d e f g h))]) (code:comment #, @elem{fast})
|
||||
(void))))
|
||||
(time (for ([i (in-range 100000)])
|
||||
(for ([elem '(a b c d e f g h)]) (code:comment #, @elem{slower})
|
||||
(void))))
|
||||
(time (let ([seq (in-list '(a b c d e f g h))])
|
||||
(for ([i (in-range 100000)])
|
||||
(for ([elem seq]) (code:comment #, @elem{slower})
|
||||
(void)))))
|
||||
]
|
||||
|
||||
In the case of @scheme[for-values] forms, a few more
|
||||
@scheme[_fast-values-clause]s provide good performance, in addition to
|
||||
the obvious variants of @scheme[_fast-clause] forms:
|
||||
|
||||
@schemeblock[
|
||||
#, @elem{a} _fast-values-clause #, @elem{is one of}
|
||||
[(_id) (in-range _expr)]
|
||||
...
|
||||
[(_id _id) (in-indexed _fast-clause)]
|
||||
[(_id ...) (in-parallel _fast-clause ...)]
|
||||
]
|
||||
|
||||
The grammars above are not complete, because the set of syntactic
|
||||
patterns that provide good performance is extensible, just like the
|
||||
set of sequence values. The documentation for a sequence constructor
|
||||
should indicate the performance impliciations of using it directly in
|
||||
a @scheme[for] @scheme[_clause].
|
14
collects/scribblings/guide/forms.scrbl
Normal file
14
collects/scribblings/guide/forms.scrbl
Normal file
|
@ -0,0 +1,14 @@
|
|||
#reader(lib "docreader.ss" "scribble")
|
||||
@require[(lib "manual.ss" "scribble")]
|
||||
@require[(lib "eval.ss" "scribble")]
|
||||
@require["guide-utils.ss"]
|
||||
|
||||
@title[#:tag "scheme-forms" #:style 'toc]{Programs and Expressions}
|
||||
|
||||
@local-table-of-contents[]
|
||||
|
||||
@include-section["module-basics.scrbl"]
|
||||
|
||||
@include-section["for.scrbl"]
|
||||
|
||||
|
|
@ -26,12 +26,7 @@ precise details to @|MzScheme| and other reference manuals.
|
|||
|
||||
@include-section["define-struct.scrbl"]
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
@section[#:tag "scheme-forms"]{Programs and Expressions}
|
||||
|
||||
@subsection{Module Basics}
|
||||
|
||||
@subsection{Definition and Expression Forms}
|
||||
@include-section["forms.scrbl"]
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
@section[#:tag "contracts"]{Contracts}
|
||||
|
|
|
@ -5,3 +5,27 @@
|
|||
|
||||
@title[#:tag "keywords"]{Keywords}
|
||||
|
||||
A @defterm{keyword} is similar to a symbol (see @secref["symbols"]),
|
||||
but its printed form is prefixed with @schemefont{#:}. Unlike a
|
||||
symbol, it's printed form is also its expression form.
|
||||
|
||||
@refdetails["mz:parse-keyword"]{the syntax of keywords}
|
||||
|
||||
@examples[
|
||||
(string->keyword "apple")
|
||||
#:apple
|
||||
(eq? #:apple (string->keyword "apple"))
|
||||
]
|
||||
|
||||
Although keywords are values, they are intented for use as special
|
||||
markers in argument lists and in certain syntactic forms.
|
||||
|
||||
@italic{Need some examples here, once we have more keyword-based
|
||||
procedures and syntax in place...}
|
||||
|
||||
Keywords should not be used simply as another kind of symbol. Use
|
||||
symbols, instead of keywords, for run-time flags and enumerations.
|
||||
|
||||
@examples[
|
||||
(code:line (bytes->path #"/usr/tmp" 'unix) (code:comment #, @t{@scheme['unix], not @scheme[#:unix]}))
|
||||
]
|
||||
|
|
|
@ -108,15 +108,15 @@ procedures. One reason is that @scheme[map], @scheme[ormap],
|
|||
list loops.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Iterative Folds and Comprehensions: @scheme[fold-for] and @scheme[list-for]}
|
||||
@section{Iterative Folds and Comprehensions: @scheme[for/fold] and @scheme[for/list]}
|
||||
|
||||
Besides iteration procedures like @scheme[foldl], Scheme provides a
|
||||
syntactic form for iteration that more closely resembles the syntax of
|
||||
other languages. The @scheme[foldl] example above can be written with
|
||||
the @scheme[fold-for] syntax as follows:
|
||||
the @scheme[for/fold] syntax as follows:
|
||||
|
||||
@interaction[
|
||||
(fold-for ([sum 0])
|
||||
(for/fold ([sum 0])
|
||||
([elem (list 1 2 3)])
|
||||
(+ sum (* elem elem)))
|
||||
]
|
||||
|
@ -136,52 +136,52 @@ EOS
|
|||
|
||||
The only significant difference is that the updating of @scheme[sum]
|
||||
and the return of @scheme[sum]'s value are implicit. Those implicit
|
||||
actions are why the form is called @scheme[fold-for] instead of just
|
||||
actions are why the form is called @scheme[for/fold] instead of just
|
||||
@scheme[for].
|
||||
|
||||
Along similar lines, the @scheme[list-for] form iterates through a list
|
||||
Along similar lines, the @scheme[for/list] form iterates through a list
|
||||
and implicitly accumulates each result into a list:
|
||||
|
||||
@interaction[
|
||||
(list-for ([i (list "peanuts" "popcorn" "crackerjack")])
|
||||
(for/list ([i (list "peanuts" "popcorn" "crackerjack")])
|
||||
(string-append i "!"))
|
||||
]
|
||||
|
||||
The @scheme[list-for] form is a @defterm{list compherension} form, as
|
||||
The @scheme[for/list] form is a @defterm{list compherension} form, as
|
||||
in Haskell, Ruby, Python, and other languages. One advantage over
|
||||
@scheme[map] is that it can iterate over more things than just lists.
|
||||
For example, @scheme[list-for] can iterate over a range of numbers:
|
||||
For example, @scheme[for/list] can iterate over a range of numbers:
|
||||
|
||||
@interaction[
|
||||
(list-for ([i (in-range 0 10)])
|
||||
(for/list ([i (in-range 0 10)])
|
||||
i)
|
||||
]
|
||||
|
||||
The @scheme[list-for] form can even iterate over a list and a range of
|
||||
The @scheme[for/list] form can even iterate over a list and a range of
|
||||
numbers in parallel:
|
||||
|
||||
@interaction[
|
||||
(list-for ([s (list "a" "b" "c")]
|
||||
(for/list ([s (list "a" "b" "c")]
|
||||
[n (in-range 0 3)])
|
||||
(if (= n 2)
|
||||
"oops!"
|
||||
s))
|
||||
]
|
||||
|
||||
Note that the binding syntax of @scheme[fold-for] and
|
||||
@scheme[list-for] is similar to that of @scheme[let] (as introduced in
|
||||
Note that the binding syntax of @scheme[for/fold] and
|
||||
@scheme[for/list] is similar to that of @scheme[let] (as introduced in
|
||||
@secref["local-binding-intro"]). In the same way that @scheme[let*]
|
||||
supports nested bindings, @scheme[list-for*] supports nested
|
||||
supports nested bindings, @scheme[for*/list] supports nested
|
||||
iterations:
|
||||
|
||||
@interaction[
|
||||
(list-for* ([s (list "a" "b" "c")]
|
||||
(for*/list ([s (list "a" "b" "c")]
|
||||
[n (list "x" "y" "z")])
|
||||
(string-append s n))
|
||||
]
|
||||
|
||||
Unlike the @scheme[list-for], the nested iteration of
|
||||
@scheme[list-for*] covers patterns with lists not as easily expressed
|
||||
Unlike the @scheme[for/list], the nested iteration of
|
||||
@scheme[for*/list] covers patterns with lists not as easily expressed
|
||||
with @scheme[map]. When procedures like @scheme[map] suffice, however,
|
||||
Scheme programmers tend to use them, partly because the syntax is
|
||||
simpler (just a procedure call).
|
||||
|
@ -194,7 +194,7 @@ see @secref["iterations+comprehensions"].
|
|||
@;------------------------------------------------------------------------
|
||||
@section{List Iteration from Scratch}
|
||||
|
||||
Although @scheme[map] and @scheme[list-for] are predefined, they are
|
||||
Although @scheme[map] and @scheme[for/list] are predefined, they are
|
||||
not primitive in any interesting sense. You can write equivalent
|
||||
iterations using a handful of list primitives.
|
||||
|
||||
|
@ -340,11 +340,11 @@ It turns out that if you write
|
|||
|
||||
@schemeblock[
|
||||
(define (my-map f lst)
|
||||
(list-for ([i lst])
|
||||
(for/list ([i lst])
|
||||
(f i)))
|
||||
]
|
||||
|
||||
then the @scheme[list-for] form in the procedure both is expanded to
|
||||
then the @scheme[for/list] form in the procedure both is expanded to
|
||||
essentially the same code as the @scheme[iter] local definition and
|
||||
use. The difference is merely syntactic convenience.
|
||||
|
||||
|
@ -446,7 +446,7 @@ context.
|
|||
That is, a named @scheme[let] binds a procedure identifier that is
|
||||
visible only in the procedure's body, and it implicitly calls the
|
||||
procedure with the values of some initial expressions. A named
|
||||
@scheme[let] looks similar to the start of @scheme[fold-for], but the
|
||||
@scheme[let] looks similar to the start of @scheme[for/fold], but the
|
||||
recursive calls in the body are explicit, and they are not constrained
|
||||
to tail position.
|
||||
|
||||
|
|
6
collects/scribblings/guide/module-basics.scrbl
Normal file
6
collects/scribblings/guide/module-basics.scrbl
Normal file
|
@ -0,0 +1,6 @@
|
|||
#reader(lib "docreader.ss" "scribble")
|
||||
@require[(lib "manual.ss" "scribble")]
|
||||
@require[(lib "eval.ss" "scribble")]
|
||||
@require["guide-utils.ss"]
|
||||
|
||||
@title{Module Basics}
|
57
collects/scribblings/guide/paths.scrbl
Normal file
57
collects/scribblings/guide/paths.scrbl
Normal file
|
@ -0,0 +1,57 @@
|
|||
#reader(lib "docreader.ss" "scribble")
|
||||
@require[(lib "manual.ss" "scribble")]
|
||||
@require[(lib "eval.ss" "scribble")]
|
||||
@require["guide-utils.ss"]
|
||||
|
||||
@title[#:tag "paths"]{Paths}
|
||||
|
||||
A @defterm{path} encapsulates a filesystem path that (potentially)
|
||||
names a file or directory. Although paths can be converted to and from
|
||||
strings and byte strings, neither strings nor byte strings are
|
||||
suitable for representing general paths. The problem is that paths are
|
||||
represented in the filesystem as either byte sequences or UTF-16
|
||||
sequences (depending on the operating systems); the sequences are not
|
||||
always human-readable, and not all sequences can be decoded to Unicode
|
||||
scalar values.
|
||||
|
||||
Despite the occasional encoding problems, most paths can be converted
|
||||
to and fom strings. Thus, procedures that accept a path argument
|
||||
always accept a string, and the printed form of a path uses the string
|
||||
decodin of the path inside @schemefont{#<path:} and @scheme{>}. The
|
||||
@scheme[display] form of a path is the same as the @scheme[display]
|
||||
form of its string encodings.
|
||||
|
||||
@examples[
|
||||
(string->path "my-data.txt")
|
||||
(file-exists? "my-data.txt")
|
||||
(file-exists? (string->path "my-data.txt"))
|
||||
(display (string->path "my-data.txt"))
|
||||
]
|
||||
|
||||
Produces that produce references to the filesystem always produce path
|
||||
values, instead of strings.
|
||||
|
||||
@examples[
|
||||
(path-replace-suffix "foo.scm" #".ss")
|
||||
]
|
||||
|
||||
Although it's sometimes tempting to directly manipulate strings that
|
||||
represent filesystem paths, correctly manipulating a path can be
|
||||
surprisingly difficult. For example, if you start under Unix with the
|
||||
aboslute path @file{/tmp/~} and take just the last part, you end up
|
||||
with @file{~}---which looks like a reference to the current user's
|
||||
home directory, instead of a relative path to a file of directory
|
||||
named @file{~}. Windows path manipulation, furthermore, is far
|
||||
trickier, because path elements like @file{aux} can have special
|
||||
meanings (see @secref["windows-path"]).
|
||||
|
||||
Use procedures like @scheme[split-path] and @scheme[build-path] to
|
||||
deconstruct and construct paths. When you must manipulate the name of
|
||||
a specific path element (i.e., a file or directory component in a
|
||||
path), use procedures like @scheme[path-element->bytes] and
|
||||
@scheme[bytes->path-element].
|
||||
|
||||
@examples[
|
||||
(build-path "easy" "file.ss")
|
||||
(split-path (build-path "easy" "file.ss"))
|
||||
]
|
Loading…
Reference in New Issue
Block a user