Adding more sequence operators

This commit is contained in:
Jay McCarthy 2010-08-12 13:13:27 -06:00
parent 3fe91f0fdd
commit 2751ace762
6 changed files with 451 additions and 11 deletions

View File

@ -1,5 +1,5 @@
#lang scheme/base
(require (only-in "private/list.rkt" split-at))
(provide first second third fourth fifth sixth seventh eighth ninth tenth
last-pair last rest
@ -113,14 +113,6 @@
(raise-type-error 'drop "non-negative exact integer" n))
(or (drop* list n) (too-large 'drop list n)))
(define (split-at list0 n0)
(unless (exact-nonnegative-integer? n0)
(raise-type-error 'split-at "non-negative exact integer" n0))
(let loop ([list list0] [n n0] [pfx '()])
(cond [(zero? n) (values (reverse pfx) list)]
[(pair? list) (loop (cdr list) (sub1 n) (cons (car list) pfx))]
[else (too-large 'take list0 n0)])))
;; take/drop-right are originally from srfi-1, uses the same lead-pointer trick
(define (take-right list n)

View File

@ -1,6 +1,7 @@
(module base "pre-base.rkt"
(#%require "hash.rkt"
(#%require "sequence.rkt"
"hash.rkt"
"list.rkt"
"string.rkt"
"stxcase-scheme.rkt"
@ -22,6 +23,7 @@
regexp-replace*
new-apply-proc)
struct
(all-from "sequence.rkt")
(all-from "hash.rkt")
(all-from "list.rkt")
(all-from-except "string.rkt"

View File

@ -1,7 +1,9 @@
(module list "pre-base.rkt"
(provide foldl
(provide split-at
foldl
foldr
remv
@ -24,6 +26,19 @@
build-list
compose)
(define (split-at list0 n0)
(unless (exact-nonnegative-integer? n0)
(raise-type-error 'split-at "non-negative exact integer" n0))
(let loop ([list list0] [n n0] [pfx '()])
(cond [(zero? n) (values (reverse pfx) list)]
[(pair? list) (loop (cdr list) (sub1 n) (cons (car list) pfx))]
[else
(raise-mismatch-error
'split-at
(format "index ~e too large for list~a: "
n0 (if (list? list0) "" " (not a proper list)"))
list0)])))
(#%require (rename "sort.rkt" raw-sort sort)
(for-syntax "stxcase-scheme.rkt"))

View File

@ -0,0 +1,247 @@
(module sequence "pre-base.rkt"
(require "list.rkt")
(define empty-seqn
(make-do-sequence
(λ ()
(values
void
void
void
(λ (pos) #f)
(λ (val) #t)
(λ (pos val) #t)))))
(define (seqn->list s)
(for/list ([v s]) v))
(define-syntax-rule (-seqn-cons vs s)
(make-do-sequence
(λ ()
(define-values (more? next) (sequence-generate s))
(values
(λ (pos)
(if (zero? pos)
vs
(next)))
(λ (pos) (if (zero? pos) 1 pos))
0
(λ (pos)
(or (zero? pos) (more?)))
(λ _ #t)
(λ _ #t)))))
(define seqn-cons
(case-lambda
[()
(error 'seqn-cons "expects a sequence to extend, but received no arguments")]
[(s)
(-seqn-cons (values) s)]
[(v s)
(-seqn-cons (values v) s)]
[vs*s
(define-values (vs sl) (split-at vs*s (sub1 (length vs*s))))
(-seqn-cons (apply values vs) (car sl))]))
(define (seqn-first s)
(define-values (more? next) (sequence-generate s))
(unless (more?)
(error 'seqn-first "expects a sequence with at least one element"))
(next))
(define (seqn-rest s)
(make-do-sequence
(λ ()
(define-values (more? next) (sequence-generate s))
(unless (more?)
(error 'seqn-rest "expects a sequence with at least one element"))
(next)
(values
(λ (pos) (next))
(λ (x) x)
0
(λ (pos) (more?))
(λ _ #t)
(λ _ #t)))))
(define (seqn-length s)
(define-values (more? next) (sequence-generate s))
(let loop ([i 0])
(if (more?)
(begin (next) (loop (add1 i)))
i)))
(define (seqn-ref s i)
(unless (and (exact-integer? i) (i . >= . 0))
(error 'seqn-ref "expects an exact non-negative index, but got ~e" i))
(define-values (more? next) (sequence-generate s))
(let loop ([n i])
(cond
[(zero? n)
(next)]
[(more?)
(next)
(loop (sub1 n))]
[else
(error 'seqn-ref "expects a sequence with at least ~e element(s)" i)])))
(define (seqn-tail s i)
(unless (and (exact-integer? i) (i . >= . 0))
(error 'seqn-tail "expects an exact non-negative index, but got ~e" i))
(make-do-sequence
(λ ()
(define-values (more? next) (sequence-generate s))
(let loop ([n i])
(unless (zero? n)
(unless (more?)
(error 'seqn-tail "expects a sequence with at least ~e element(s)" i))
(next)
(loop (sub1 n))))
(values
(λ (pos) (next))
(λ (x) x)
0
(λ (pos) (more?))
(λ _ #t)
(λ _ #t)))))
(define (-seqn-append s0 l)
(if (null? l)
s0
(make-do-sequence
(λ ()
(define remaining l)
(define (next-pos pos)
(cond
[(more?)
#t]
[(null? remaining)
#f]
[else
(let*-values ([(s1) (car remaining)]
[(next-more? next-next) (sequence-generate s1)])
(set! more? next-more?)
(set! next next-next)
(set! remaining (cdr remaining))
(next-pos pos))]))
(define-values (more? next) (sequence-generate s0))
(values
(λ (pos) (next))
(λ (x) x)
0
next-pos
(λ _ #t)
(λ _ #t))))))
(define (seqn-append . l)
(unless (andmap sequence? l)
(error 'seqn-append "expects only sequence arguments, given ~e" l))
(-seqn-append empty-seqn l))
(define (seqn-map f s)
(unless (procedure? f)
(error 'seqn-map "expects a procedure as the first argument, given ~e" f))
(make-do-sequence
(λ ()
(define-values (more? next) (sequence-generate s))
(values
(λ (pos) (call-with-values next f))
(λ (x) x)
0
(λ (pos) (more?))
(λ _ #t)
(λ _ #t)))))
(define (seqn-andmap f s)
(define-values (more? next) (sequence-generate s))
(let loop ()
(if (more?)
(and (call-with-values next f) (loop))
#t)))
(define (seqn-ormap f s)
(define-values (more? next) (sequence-generate s))
(let loop ()
(if (more?)
(or (call-with-values next f) (loop))
#f)))
(define (seqn-for-each f s)
(define-values (more? next) (sequence-generate s))
(let loop ()
(when (more?)
(call-with-values next f)
(loop))))
(define (seqn-fold f i s)
(define-values (more? next) (sequence-generate s))
(let loop ([i i])
(if (more?)
(loop (call-with-values next (λ e (apply f i e))))
i)))
(define (seqn-filter f s)
(unless (procedure? f)
(error 'seqn-filter "expects a procedure as the first argument, given ~e" f))
(make-do-sequence
(λ ()
(define-values (more? next) (sequence-generate s))
(define next-vs #f)
(define (next-pos pos)
(if (more?)
(call-with-values next
(λ vs
(if (apply f vs)
(begin (set! next-vs vs)
#t)
(next-pos pos))))
#f))
(values
(λ (pos) (apply values next-vs))
(λ (x) x)
0
next-pos
(λ _ #t)
(λ _ #t)))))
(define (seqn-add-between s e)
(make-do-sequence
(λ ()
(define-values (more? next) (sequence-generate s))
(values
(λ (pos)
(if pos
(next)
e))
not
#t
(λ (pos)
(if pos
(more?)
#t))
(λ _ #t)
(λ _ #t)))))
(define (seqn-count s)
(define-values (more? next) (sequence-generate s))
(let loop ([n 0])
(if (more?)
(begin (next) (loop (add1 n)))
n)))
(provide empty-seqn
seqn->list
seqn-cons
seqn-first
seqn-rest
seqn-length
seqn-ref
seqn-tail
seqn-append
seqn-map
seqn-andmap
seqn-ormap
seqn-for-each
seqn-fold
seqn-filter
seqn-add-between
seqn-count))

View File

@ -63,6 +63,89 @@ each element in the sequence.
@defproc[(sequence? [v any/c]) boolean?]{ Return @scheme[#t] if
@scheme[v] can be used as a sequence, @scheme[#f] otherwise.}
@defthing[empty-seqn sequence?]{ A sequence with no elements. }
@defthing[(seqn->list [s (sequence/c any/c)]) list?]{ Returns a list whose
elements are the elements of the @scheme[s]. If @scheme[s] is infinite, this
function does not terminate. }
@defproc[(seqn-cons [v any/c]
...
[s sequence?])
sequence?]{
Returns a sequence whose first element is @scheme[(values v ...)] and whose
remaining elements are the same as @scheme[s].
}
@defproc[(seqn-first [s sequence?])
(values any/c ...)]{
Returns the first element of @scheme[s].}
@defproc[(seqn-rest [s sequence?])
sequence?]{
Returns a sequence equivalent to @scheme[s], except the first element is omitted.}
@defproc[(seqn-length [s sequence?])
exact-nonnegative-integer?]{
Returns the number of elements of @scheme[s]. If @scheme[s] is infinite, this
function does not terminate. }
@defproc[(seqn-ref [s sequence?] [i exact-nonnegative-integer?])
(values any/c ...)]{
Returns the @scheme[i]th element of @scheme[s].}
@defproc[(seqn-tail [s sequence?] [i exact-nonnegative-integer?])
sequence?]{
Returns a sequence equivalent to @scheme[s], except the first @scheme[i] elements are omitted.}
@defproc[(seqn-append [s sequence?] ...)
sequence?]{
Returns a sequence that contains all elements of each sequence in the order they appear in the original sequences. The
new sequence is constructed lazily. }
@defproc[(seqn-map [f (-> any/c ..._0 (values any/c ..._1))]
[s (sequence/c any/c ..._0)])
(sequence/c any/c ..._1)]{
Returns a sequence that contains @scheme[f] applied to each element of @scheme[s]. The new sequence is constructed lazily. }
@defproc[(seqn-andmap [f (-> any/c ..._0 boolean?)]
[s (sequence/c any/c ..._0)])
boolean?]{
Returns @scheme[#t] if @scheme[f] returns a true result on every element of @scheme[s]. If @scheme[s] is infinite and @scheme[f] never
returns a false result, this function does not terminate. }
@defproc[(seqn-ormap [f (-> any/c ..._0 boolean?)]
[s (sequence/c any/c ..._0)])
boolean?]{
Returns @scheme[#t] if @scheme[f] returns a true result on some element of @scheme[s]. If @scheme[s] is infinite and @scheme[f] never
returns a true result, this function does not terminate. }
@defproc[(seqn-for-each [f (-> any/c ..._0 any)]
[s (sequence/c any/c ..._0)])
(void)]{
Applies @scheme[f] to each element of @scheme[s]. If @scheme[s] is infinite, this function does not terminate. }
@defproc[(seqn-fold [f (-> any/c any/c ..._0 any/c)]
[i any/c]
[s (sequence/c any/c ..._0)])
(void)]{
Folds @scheme[f] over each element of @scheme[s] with @scheme[i] as the initial accumulator. If @scheme[s] is infinite, this function does not terminate. }
@defproc[(seqn-filter [f (-> any/c ..._0 boolean?)]
[s (sequence/c any/c ..._0)])
(sequence/c any/c ..._0)]{
Returns a sequence whose elements are the elements of @scheme[s] for which @scheme[f] returns a true result. Although the new sequence is constructed
lazily, if @scheme[s] has an infinite number of elements where @scheme[f] returns a false result in between two elements where @scheme[f] returns a true result
then operations on this sequence will not terminate during that infinite sub-sequence. }
@defproc[(seqn-add-between [s sequence?] [e any/c])
sequence?]{
Returns a sequence whose elements are the elements of @scheme[s] except in between each is @scheme[e]. The new sequence is constructed lazily. }
@defproc[(seqn-count [s sequence?])
exact-nonnegative-integer?]{
Returns the number of elements in @scheme[s]. If @scheme[s] is infinite, this function does not terminate. }
@defproc*[([(in-range [end number?]) sequence?]
[(in-range [start number?] [end number?] [step number? 1]) sequence?])]{
Returns a sequence whose elements are numbers. The single-argument

View File

@ -288,4 +288,105 @@
(list (maker) (maker) (maker)
(maker) (maker) (maker))))
;; New operators
(require racket/private/sequence)
(test '(0 1 2) 'seqn->list (seqn->list (in-range 3)))
(arity-test seqn->list 1 1)
(err/rt-test (seqn->list 1))
(test '() 'empty-seqn (seqn->list empty-seqn))
; XXX How do I check rest arity?
(test '(0 1 2) 'seqn-cons (seqn->list (seqn-cons 0 (in-range 1 3))))
(test '((0 1)) 'seqn-cons
(for/list ([(a b) (seqn-cons 0 1 empty-seqn)])
(list a b)))
(arity-test seqn-first 1 1)
(err/rt-test (seqn-first 1))
(test 0 'seqn-first (seqn-first (in-naturals)))
(test #t
'seqn-first
(equal? (list 0 1)
(call-with-values
(λ ()
(seqn-first (seqn-cons 0 1 empty-seqn)))
(λ args args))))
(arity-test seqn-rest 1 1)
(test '(1 2) 'seqn-rest (seqn->list (seqn-rest (in-range 3))))
(arity-test seqn-length 1 1)
(err/rt-test (seqn-length 1))
(test 3 'seqn-length (seqn-length (in-range 3)))
(test 3 'seqn-length (seqn-length #hasheq((1 . 'a) (2 . 'b) (3 . 'c))))
(arity-test seqn-ref 2 2)
(err/rt-test (seqn-ref 2 0))
(err/rt-test (seqn-ref (in-naturals) -1) exn:fail?)
(err/rt-test (seqn-ref (in-naturals) 1.0) exn:fail?)
(test 0 'seqn-ref (seqn-ref (in-naturals) 0))
(test 1 'seqn-ref (seqn-ref (in-naturals) 1))
(test 25 'seqn-ref (seqn-ref (in-naturals) 25))
(arity-test seqn-tail 2 2)
(err/rt-test (seqn-tail (in-naturals) -1) exn:fail?)
(err/rt-test (seqn-tail (in-naturals) 1.0) exn:fail?)
(test 4 'seqn-ref (seqn-ref (seqn-tail (in-naturals) 4) 0))
(test 5 'seqn-ref (seqn-ref (seqn-tail (in-naturals) 4) 1))
(test 29 'seqn-ref (seqn-ref (seqn-tail (in-naturals) 4) 25))
; XXX Check for rest
(err/rt-test (seqn-append 1) exn:fail?)
(err/rt-test (seqn-append (in-naturals) 1) exn:fail?)
(test '() 'seqn-append (seqn->list (seqn-append)))
(test 5 'seqn-append (seqn-ref (seqn-append (in-naturals)) 5))
(test 5 'seqn-append (seqn-ref (seqn-append (in-range 3) (in-range 3 10)) 5))
(arity-test seqn-map 2 2)
(err/rt-test (seqn-map 2 (in-naturals)) exn:fail?)
(test '(1 2 3) 'seqn-map (seqn->list (seqn-map add1 (in-range 3))))
(test 3 'seqn-map (seqn-ref (seqn-map add1 (in-naturals)) 2))
(arity-test seqn-andmap 2 2)
(err/rt-test (seqn-andmap 2 (in-naturals)))
(test #t 'seqn-andmap (seqn-andmap even? (seqn-cons 2 empty-seqn)))
(test #f 'seqn-andmap (seqn-andmap even? (in-naturals)))
(arity-test seqn-ormap 2 2)
(err/rt-test (seqn-ormap 2 (in-naturals)))
(test #t 'seqn-ormap (seqn-ormap even? (seqn-cons 2 empty-seqn)))
(test #f 'seqn-ormap (seqn-ormap even? (seqn-cons 1 empty-seqn)))
(test #t 'seqn-ormap (seqn-ormap even? (in-naturals)))
(arity-test seqn-for-each 2 2)
(err/rt-test (seqn-for-each 2 (in-naturals)))
(test (vector 0 1 2)
'seqn-for-each
(let ([v (vector #f #f #f)])
(seqn-for-each (λ (i) (vector-set! v i i)) (in-range 3))
v))
(arity-test seqn-fold 3 3)
(err/rt-test (seqn-fold 2 (in-naturals) 0))
(test 6 'seqn-fold (seqn-fold + 0 (in-range 4)))
(arity-test seqn-filter 2 2)
(err/rt-test (seqn-filter 2 (in-naturals)) exn:fail?)
(test 4 'seqn-filter (seqn-ref (seqn-filter even? (in-naturals)) 2))
(arity-test seqn-add-between 2 2)
(test 0 'seqn-add-between (seqn-ref (seqn-add-between (in-naturals) #t) 0))
(test #t 'seqn-add-between (seqn-ref (seqn-add-between (in-naturals) #t) 1))
(test 1 'seqn-add-between (seqn-ref (seqn-add-between (in-naturals) #t) 2))
(test #t 'seqn-add-between (seqn-ref (seqn-add-between (in-naturals) #t) 3))
(arity-test seqn-count 1 1)
(test 0 'seqn-count (seqn-count empty-seqn))
(test 1 'seqn-count (seqn-count (in-range 1)))
(test 10 'seqn-count (seqn-count (in-range 10)))
(let ([r (random 100)])
(test r 'seqn-count (seqn-count (in-range r))))
(report-errs)