diff --git a/lib/srfi/127.scm b/lib/srfi/127.scm new file mode 100644 index 00000000..7d86e548 --- /dev/null +++ b/lib/srfi/127.scm @@ -0,0 +1,203 @@ + +(define eof-object (read-char (open-input-string ""))) + +(define (generator->lseq gen) + (let ((val (gen))) + (if (eof-object? val) + '() + (cons val gen)))) + +(define lseq-car car) +(define lseq-first car) + +(define (lseq-cdr lseq) + (if (procedure? (cdr lseq)) + (let ((val ((cdr lseq)))) + (cond + ((eof-object? val) + (set-cdr! lseq '()) + '()) + (else + (let ((cell (cons val (cdr lseq)))) + (set-cdr! lseq cell) + cell)))) + (cdr lseq))) + +(define lseq-rest lseq-cdr) + +(define (lseq? x) + (if (pair? x) + (or (procedure? (cdr x)) + (lseq? (cdr x))) + (null? x))) + +(define (lseq=? eq lseq1 lseq2) + (let lp ((ls1 lseq1) (ls2 lseq2)) + (cond + ((null? ls1) (null? ls2)) + ((null? ls2) #f) + ((eq (lseq-car ls1) (lseq-car ls2)) + (lp (lseq-cdr ls1) (lseq-cdr ls2))) + (else #f)))) + +(define (lseq-drop lseq k) + (if (positive? k) + (lseq-drop (lseq-cdr lseq) (- k 1)) + lseq)) + +(define (lseq-drop-while pred lseq) + (if (and (pair? lseq) (pred (lseq-car lseq))) + (lseq-drop-while pred (lseq-cdr lseq)) + lseq)) + +(define (lseq-take lseq k) + (generator->lseq + (lambda () + (if (positive? k) + (let ((val (lseq-car lseq))) + (set! lseq (lseq-cdr lseq)) + (set! k (- k 1)) + val) + eof-object)))) + +(define (lseq-take-while pred lseq) + (generator->lseq + (lambda () + (if (and (pair? lseq) (pred (lseq-car lseq))) + (let ((val (lseq-car lseq))) + (set! lseq (lseq-cdr lseq)) + val) + eof-object)))) + +(define (lseq-ref lseq k) + (lseq-first (lseq-drop lseq k))) + +(define (lseq-realize lseq) + (let lp ((lseq lseq) (ls '())) + (if (null? lseq) + (reverse ls) + (lp (lseq-cdr lseq) (cons (lseq-car lseq) ls))))) + +(define (lseq->generator lseq) + (lambda () + (if (null? lseq) + eof-object + (let ((val (lseq-car lseq))) + (set! lseq (lseq-cdr lseq)) + val)))) + +(define (lseq-length lseq) + (let lp ((lseq lseq) (len 0)) + (if (null? lseq) len (lp (lseq-cdr lseq) (+ len 1))))) + +(define (lseq-append . lseqs) + (if (every null? lseqs) + '() + (let ((lseq1 (car lseqs)) + (ls (cdr lseqs))) + (define (gen) + (cond + ((pair? lseq1) + (let ((val (lseq-car lseq1))) + (set! lseq1 (cdr lseq1)) + val)) + ((pair? ls) + (set! lseq1 (car ls)) + (set! ls (cdr ls)) + (gen)) + (else + eof-object))) + (generator->lseq gen)))) + +(define (lseq-zip . lseqs) + (generator->lseq + (lambda () + (if (any null? lseqs) + eof-object + (let ((val (map lseq-car lseqs))) + (set! lseqs (map lseq-cdr lseqs)) + val))))) + +(define (lseq-map proc lseq . o) + (if (or (null? lseq) (any null? o)) + '() + (let ((gen (lseq->generator lseq))) + (generator->lseq + (if (null? o) + (lambda () + (let ((val (gen))) + (if (eof-object? val) + val + (proc val)))) + (let ((gens (map lseq->generator o))) + (lambda () + (let ((val (gen)) + (vals (map (lambda (f) (f)) gens))) + (if (or (eof-object? val) + (any eof-object? vals)) + eof-object + (apply proc val vals)))))))))) + +(define (lseq-for-each proc lseq . o) + (let lp ((lseq (apply lseq-map proc lseq o))) + (when (pair? lseq) + (lseq-car lseq) + (lp (lseq-cdr lseq))))) + +(define (lseq-filter pred lseq) + (let ((gen (lseq->generator lseq))) + (define (filt) + (let ((val (gen))) + (if (or (eof-object? val) (pred val)) + val + (filt)))) + (generator->lseq filt))) + +(define (lseq-remove pred lseq) + (lseq-filter (lambda (x) (not (pred x))) lseq)) + +(define (lseq-find-tail pred lseq) + (and (pair? lseq) + (if (pred (lseq-car lseq)) + lseq + (lseq-find-tail pred (lseq-cdr lseq))))) + +(define (lseq-find pred lseq) + (cond ((lseq-find-tail pred lseq) => lseq-car) (else #f))) + +(define (lseq-any pred lseq . o) + (if (null? o) + (let any ((lseq lseq)) + (and (pair? lseq) + (or (pred (lseq-car lseq)) + (any (lseq-cdr lseq))))) + (let any ((lseqs (cons lseq o))) + (and (every pair? lseqs) + (or (apply pred (map lseq-car lseqs)) + (any (map lseq-cdr lseqs))))))) + +(define (lseq-every pred lseq . o) + (if (null? o) + (let every ((lseq lseq) (last #t)) + (if (null? lseq) + last + (let ((val (pred (lseq-car lseq)))) + (and val (every (lseq-cdr lseq) val))))) + (let every ((lseqs (cons lseq o)) (last #t)) + (if (any null? lseqs) + last + (let ((val (apply pred (map lseq-car lseqs)))) + (and val (every (map lseq-cdr lseqs) val))))))) + +(define (lseq-index pred lseq . o) + (let ((i -1)) + (and (apply lseq-any (lambda args (set! i (+ i 1)) (apply pred args)) lseq o) + i))) + +(define (lseq-member elt lseq . o) + (let* ((eq (if (pair? o) (car o) equal?)) + (res (lseq-drop-while (lambda (x) (not (eq x elt))) lseq))) + (and (pair? res) res))) + +(define (lseq-memq elt lseq) (lseq-member elt lseq eq?)) +(define (lseq-memv elt lseq) (lseq-member elt lseq eqv?)) diff --git a/lib/srfi/127.sld b/lib/srfi/127.sld new file mode 100644 index 00000000..986b2471 --- /dev/null +++ b/lib/srfi/127.sld @@ -0,0 +1,26 @@ + +(define-library (srfi 127) + (import (scheme base) (srfi 1)) + (export + ;; Constructors + generator->lseq + ;; Predicates + lseq? lseq=? + ;; Selectors + lseq-car lseq-cdr + lseq-first lseq-rest lseq-ref + lseq-take lseq-drop + ;; The whole lazy sequence + lseq-realize lseq->generator + lseq-length + lseq-append lseq-zip + ;; Mapping and filtering + lseq-map lseq-for-each + lseq-filter lseq-remove + ;; Searching + lseq-find lseq-find-tail + lseq-any lseq-every + lseq-index + lseq-take-while lseq-drop-while + lseq-member lseq-memq lseq-memv) + (include "127.scm")) diff --git a/lib/srfi/127/test.sld b/lib/srfi/127/test.sld new file mode 100644 index 00000000..6efb0fad --- /dev/null +++ b/lib/srfi/127/test.sld @@ -0,0 +1,197 @@ + +(define-library (srfi 127 test) + (import (scheme base) (srfi 127) (chibi test)) + (export run-tests) + (begin + ;; Make-generator for tests cloned from SRFI 121 + (define (make-generator . args) + (lambda () + (if (null? args) + (eof-object) + (let ((next (car args))) + (set! args (cdr args)) + next)))) + + ;; Make-lseq creates an lseq, like list, but guarantees the use of + ;; a generator. + (define (make-lseq . args) + (generator->lseq (apply make-generator args))) + + (define (factorial n) + (cond + ((< n 0) #f) + ((= n 0) 1) + (else (* n (factorial (- n 1)))))) + + (define (run-tests) + (test-group "srfi-127: lseqs" + (test-group "lseqs/constructor" + (let ((one23 (make-lseq 1 2 3))) + (test 1 (car one23)) + (test-assert (procedure? (cdr one23))) + (test '(1 2 3) (lseq-realize one23))) + ) + + (test-group "lseqs/predicates" + (test-assert (lseq? '())) + (test-assert (lseq? '(1 2 3))) + (test-assert (lseq? (make-lseq 1 2 3))) + (test-assert (lseq? (cons 'x (lambda () 'x)))) + + (test-assert (lseq=? = '() '())) + (test-assert (lseq=? = '(1 2 3) '(1 2 3))) + (test-assert (lseq=? = (make-lseq 1 2 3) + (make-lseq 1 2 3))) + (test-assert (lseq=? = (make-lseq 1 2 3) '(1 2 3))) + ) + + (test-group "lseqs/selectors" + (test-error (lseq-car (make-generator))) + (test 1 (lseq-car (make-lseq 1 2 3))) + (test 1 (lseq-car '(1 2 3))) + (test-error (lseq-car 2)) + + (test-error (lseq-first (make-generator))) + (test 1 (lseq-first (make-lseq 1 2 3))) + (test 1 (lseq-first '(1 2 3))) + (test-error (lseq-first 2)) + + (test-error (lseq-cdr (make-generator))) + (test 2 (lseq-cdr '(1 . 2))) + (test 2 (lseq-car (lseq-cdr '(1 2 3)))) + (test 2 (lseq-car (lseq-cdr (make-lseq 1 2 3)))) + + (test-error (lseq-rest (make-generator))) + (test 2 (lseq-rest '(1 . 2))) + (test 2 (lseq-car (lseq-rest '(1 2 3)))) + (test 2 (lseq-car (lseq-rest (make-lseq 1 2 3)))) + (test-error (lseq-rest 2)) + + (test-error (lseq-ref '() 0)) + (test 1 (lseq-ref '(1) 0)) + (test 2 (lseq-ref '(1 2) 1)) + (test-error (lseq-ref (make-lseq) 0)) + (test 1 (lseq-ref (make-lseq 1) 0)) + (test 1 (lseq-ref (make-lseq 1 2) 0)) + (test 2 (lseq-ref (make-lseq 1 2) 1)) + + (test-error (lseq-take '() 1)) + (test-error (lseq-take (make-lseq) 1)) + (test-assert (procedure? (cdr (lseq-take '(1 2 3 4 5) 3)))) ; test laziness + (test '(1 2 3) (lseq-realize (lseq-take '(1 2 3 4 5) 3))) + + (test-error (lseq-drop '() 1)) + (test-error (lseq-drop (make-lseq 1) 2)) + (test '(3 4 5) (lseq-realize (lseq-drop '(1 2 3 4 5) 2))) + (test '(3 4 5) (lseq-realize (lseq-drop (make-lseq 1 2 3 4 5) 2))) + ) + + (test-group "lseqs/whole" + (test '() (lseq-realize '())) + (test '(1 2 3) (lseq-realize '(1 2 3))) + (test '() (lseq-realize (make-lseq))) + (test '(1 2 3) (lseq-realize (make-lseq 1 2 3))) + + (let ((g (lseq->generator '(1 2 3)))) + (test 1 (g)) + (test 2 (g)) + (test 3 (g)) + (test-assert (eof-object? (g)))) + + (let ((g (lseq->generator (make-lseq 1 2 3)))) + (test 1 (g)) + (test 2 (g)) + (test 3 (g)) + (test-assert (eof-object? (g)))) + + (test 0 (lseq-length '())) + (test 3 (lseq-length '(1 2 3))) + (test 3 (lseq-length (make-lseq 1 2 3))) + + (test '(1 2 3 a b c) (lseq-realize (lseq-append '(1 2 3) '(a b c)))) + (let ((one23abc (lseq-append (make-lseq 1 2 3) (make-lseq 'a 'b 'c)))) + (test-assert (procedure? (cdr one23abc))) + (test-assert (lseq-realize one23abc))) + + (let ((one2345 (make-lseq 1 2 3 4 5)) + (oddeven (make-lseq 'odd 'even 'odd 'even 'odd 'even 'odd 'even))) + (test '((one 1 odd) (two 2 even) (three 3 odd)) + (lseq-realize (lseq-zip '(one two three) one2345 oddeven)))) + ) + + (test-group "lseqs/mapping" + (test '() (lseq-map - '())) + (test '(-1 -2 -3) (lseq-realize (lseq-map - '(1 2 3)))) + (test '(-1 -2 -3) (lseq-realize (lseq-map - (make-lseq 1 2 3)))) + (test-assert (procedure? (cdr (lseq-map - '(1 2 3))))) + + (let* ((output '()) + (out! (lambda (x) (set! output (cons x output))))) + (lseq-for-each out! '()) + (test '() output) + (lseq-for-each out! '(a b c)) + (test '(c b a) output) + (lseq-for-each out! (make-lseq 1 2 3)) + (test '(3 2 1 c b a) output)) + + (test '() (lseq-filter odd? '())) + + (let ((odds (lseq-filter odd? '(1 2 3 4 5)))) + (test-assert (procedure? (cdr odds))) + (test '(1 3 5) (lseq-realize odds)) + (test '(1 3 5) (lseq-realize (lseq-filter odd? (make-lseq 1 2 3 4 5))))) + + (test '() (lseq-remove even? '())) + (let ((odds (lseq-remove even? '(1 2 3 4 5)))) + (test-assert (procedure? (cdr odds))) + (test '(1 3 5) (lseq-realize odds)) + (test '(1 3 5) (lseq-realize (lseq-remove even? (make-lseq 1 2 3 4 5)))))) + + (test-group "lseqs/searching" + (test 4 (lseq-find even? '(3 1 4 1 5 9 2 6))) + (test 4 (lseq-find even? (make-lseq 3 1 4 1 5 9 2 6))) + (test #f (lseq-find negative? (make-lseq 1 2 3 4 5))) + + (test '(-8 -5 0 0) (lseq-realize (lseq-find-tail even? '(3 1 37 -8 -5 0 0)))) + (test '(-8 -5 0 0) (lseq-realize (lseq-find-tail even? + (make-lseq 3 1 37 -8 -5 0 0)))) + (test #f (lseq-find-tail even? '())) + (test #f (lseq-find-tail negative? (make-lseq 1 2 3 4 5))) + + (test '(2 18) (lseq-realize (lseq-take-while even? '(2 18 3 10 22 9)))) + (test '(2 18) (lseq-realize (lseq-take-while even? + (make-lseq 2 18 3 10 22 9)))) + (test '(2 18) (lseq-realize (lseq-take-while even? + (make-lseq 2 18 3 10 22 9)))) + + (test '(3 10 22 9) (lseq-drop-while even? '(2 18 3 10 22 9))) + (test '(3 10 22 9) (lseq-realize (lseq-drop-while even? + (make-lseq 2 18 3 10 22 9)))) + + (test #t (lseq-any integer? '(a 3 b 2.7))) + (test #t (lseq-any integer? (make-lseq 'a 3 'b 2.7))) + (test #f (lseq-any integer? '(a 3.1 b 2.7))) + (test #f (lseq-any integer? (make-lseq 'a 3.1 'b 2.7))) + (test #t (lseq-any < '(3 1 4 1 5) '(2 7 1 8 2))) + (test 6 (lseq-any factorial '(-1 -2 3 4))) + (test 6 (lseq-any factorial (make-lseq -1 -2 3 4))) + + (test 24 (lseq-every factorial '(1 2 3 4))) + (test 24 (lseq-every factorial (make-lseq 1 2 3 4))) + + (test 2 (lseq-index even? '(3 1 4 1 5 9))) + (test 1 (lseq-index < '(3 1 4 1 5 9 2 5 6) '(2 7 1 8 2))) + (test #f (lseq-index = '(3 1 4 1 5 9 2 5 6) '(2 7 1 8 2))) + + (test '(a b c) (lseq-realize (lseq-memq 'a '(a b c)))) + (test '(a b c) (lseq-realize (lseq-memq 'a (make-lseq 'a 'b 'c)))) + (test #f (lseq-memq 'a (make-lseq 'b 'c 'd))) + (test #f (lseq-memq (list 'a) '(b c d))) + (test #f (lseq-memq (list 'a) (make-lseq 'b 'c 'd))) + + (test '(101 102) (lseq-realize (lseq-memv 101 (make-lseq 100 101 102)))) + + (test '((a) c) (lseq-realize (lseq-member (list 'a) (make-lseq 'b '(a) 'c)))) + (test '(2 3) (lseq-realize (lseq-member 2.0 (make-lseq 1 2 3) =))) + ))) + )) diff --git a/tests/lib-tests.scm b/tests/lib-tests.scm index edceb688..111bb4c3 100644 --- a/tests/lib-tests.scm +++ b/tests/lib-tests.scm @@ -16,6 +16,7 @@ (rename (srfi 116 test) (run-tests run-srfi-116-tests)) (rename (srfi 117 test) (run-tests run-srfi-117-tests)) (rename (srfi 121 test) (run-tests run-srfi-121-tests)) + (rename (srfi 127 test) (run-tests run-srfi-127-tests)) (rename (srfi 128 test) (run-tests run-srfi-128-tests)) (rename (srfi 129 test) (run-tests run-srfi-129-tests)) (rename (srfi 130 test) (run-tests run-srfi-130-tests)) @@ -70,6 +71,7 @@ (run-srfi-116-tests) (run-srfi-117-tests) (run-srfi-121-tests) +(run-srfi-127-tests) (run-srfi-128-tests) (run-srfi-129-tests) (run-srfi-130-tests)