#lang s-exp "../kernel.rkt"
(provide foldl
foldr
remv
remq
remove
remv*
remq*
remove*
memf
assf
findf
assq
assv
assoc
filter
build-vector
build-string
build-list
compose
compose1
)
(require (only-in "../unsafe/ops.rkt" unsafe-car unsafe-cdr))
(define (do-remove who item list equal?)
(unless (list? list)
(raise-type-error who "list" list))
(let loop ([list list])
(cond [(null? list) null]
[(equal? item (car list)) (cdr list)]
[else (cons (car list) (loop (cdr list)))])))
(define remove
(case-lambda
[(item list) (do-remove 'remove item list equal?)]
[(item list equal?)
(unless (and (procedure? equal?)
(procedure-arity-includes? equal? 2))
(raise-type-error 'remove "procedure (arity 2)" equal?))
(do-remove 'remove item list equal?)]))
(define (remq item list)
(do-remove 'remq item list eq?))
(define (remv item list)
(do-remove 'remv item list eqv?))
(define (do-remove* who l r equal?)
(unless (list? l)
(raise-type-error who "list" l))
(unless (list? r)
(raise-type-error who "list" r))
(let rloop ([r r])
(cond
[(null? r) null]
[else (let ([first-r (car r)])
(let loop ([l-rest l])
(cond
[(null? l-rest) (cons first-r (rloop (cdr r)))]
[(equal? (car l-rest) first-r) (rloop (cdr r))]
[else (loop (cdr l-rest))])))])))
(define remove*
(case-lambda
[(l r) (do-remove* 'remove* l r equal?)]
[(l r equal?)
(unless (and (procedure? equal?)
(procedure-arity-includes? equal? 2))
(raise-type-error 'remove* "procedure (arity 2)" equal?))
(do-remove* 'remove* l r equal?)]))
(define (remq* l r)
(do-remove* 'remq* l r eq?))
(define (remv* l r)
(do-remove* 'remv* l r eqv?))
(define (memf f list)
(unless (and (procedure? f) (procedure-arity-includes? f 1))
(raise-type-error 'memf "procedure (arity 1)" f))
(let loop ([l list])
(cond
[(null? l) #f]
[(not (pair? l))
(raise-mismatch-error 'memf
"not a proper list: "
list)]
[else (if (f (car l)) l (loop (cdr l)))])))
(define (findf f list)
(unless (and (procedure? f) (procedure-arity-includes? f 1))
(raise-type-error 'findf "procedure (arity 1)" f))
(let loop ([l list])
(cond
[(null? l) #f]
[(not (pair? l))
(raise-mismatch-error 'findf
"not a proper list: "
list)]
[else (let ([a (car l)])
(if (f a)
a
(loop (cdr l))))])))
(define (bad-list who orig-l)
(raise-mismatch-error who
"not a proper list: "
orig-l))
(define (bad-item who a orig-l)
(raise-mismatch-error who
"non-pair found in list: "
a
" in "
orig-l))
(define-values (assq assv assoc assf)
(let ()
(define-syntax-rule (assoc-loop who x orig-l is-equal?)
(let loop ([l orig-l][t orig-l])
(cond
[(pair? l)
(let ([a (unsafe-car l)])
(if (pair? a)
(if (is-equal? x (unsafe-car a))
a
(let ([l (unsafe-cdr l)])
(cond
[(pair? l)
(let ([a (unsafe-car l)])
(if (pair? a)
(if (is-equal? x (unsafe-car a))
a
(let ([t (unsafe-cdr t)]
[l (unsafe-cdr l)])
(if (eq? l t)
(bad-list who orig-l)
(loop l t))))
(bad-item who a orig-l)))]
[(null? l) #f]
[else (bad-list who orig-l)])))
(bad-item who a orig-l)))]
[(null? l) #f]
[else (bad-list who orig-l)])))
(let ([assq
(lambda (x l)
(assoc-loop 'assq x l eq?))]
[assv
(lambda (x l)
(assoc-loop 'assv x l eqv?))]
[assoc
(case-lambda
[(x l) (assoc-loop 'assoc x l equal?)]
[(x l is-equal?)
(unless (and (procedure? is-equal?)
(procedure-arity-includes? is-equal? 2))
(raise-type-error 'assoc "procedure (arity 2)" is-equal?))
(assoc-loop 'assoc x l is-equal?)])]
[assf
(lambda (f l)
(unless (and (procedure? f) (procedure-arity-includes? f 1))
(raise-type-error 'assf "procedure (arity 1)" f))
(assoc-loop 'assf #f l (lambda (_ a) (f a))))])
(values assq assv assoc assf))))
(define (mapadd f l last)
(let loop ([l l])
(if (null? l)
(list last)
(cons (f (car l)) (loop (cdr l))))))
(define (check-fold name proc init l more)
(unless (procedure? proc)
(apply raise-type-error name "procedure" 0 proc init l more))
(unless (list? l)
(apply raise-type-error name "list" 2 proc init l more))
(if (null? more)
(unless (procedure-arity-includes? proc 2)
(raise-mismatch-error name "given procedure does not accept 2 arguments: " proc))
(let ([len (length l)])
(let loop ([more more][n 3])
(unless (null? more)
(unless (list? (car more))
(apply raise-type-error name "list" n proc init l more))
(unless (= len (length (car more)))
(raise-mismatch-error name
"given list does not have the same size as the first list: "
(car more)))
(loop (cdr more) (add1 n))))
(unless (procedure-arity-includes? proc (+ 2 (length more)))
(raise-mismatch-error name
(format "given procedure does not accept ~a arguments: "
(+ 2 (length more)))
proc)))))
(define foldl
(case-lambda
[(f init l)
(check-fold 'foldl f init l null)
(let loop ([init init] [l l])
(if (null? l) init (loop (f (car l) init) (cdr l))))]
[(f init l . ls)
(check-fold 'foldl f init l ls)
(let loop ([init init] [ls (cons l ls)])
(if (pair? (car ls)) (loop (apply f (mapadd car ls init)) (map cdr ls))
init))]))
(define foldr
(case-lambda
[(f init l)
(check-fold 'foldr f init l null)
(let loop ([init init] [l l])
(if (null? l)
init
(f (car l) (loop init (cdr l)))))]
[(f init l . ls)
(check-fold 'foldr f init l ls)
(let loop ([ls (cons l ls)])
(if (pair? (car ls)) (apply f (mapadd car ls (loop (map cdr ls))))
init))]))
(define (filter f list)
(unless (and (procedure? f)
(procedure-arity-includes? f 1))
(raise-type-error 'filter "procedure (arity 1)" f))
(unless (list? list)
(raise-type-error 'filter "proper list" list))
(let loop ([l list] [result null])
(if (null? l)
(reverse result)
(loop (cdr l) (if (f (car l)) (cons (car l) result) result)))))
(define (build-vector n fcn)
(unless (exact-nonnegative-integer? n)
(raise-type-error 'build-vector "exact-nonnegative-integer" n))
(unless (and (procedure? fcn)
(procedure-arity-includes? fcn 1))
(raise-type-error 'build-vector "procedure (arity 1)" fcn))
(let ([vec (make-vector n)])
(let loop ((i 0))
(if (= i n)
vec
(begin (vector-set! vec i (fcn i)) (loop (add1 i)))))))
(define (build-string n fcn)
(unless (exact-nonnegative-integer? n)
(raise-type-error 'build-string "exact-nonnegative-integer" n))
(unless (and (procedure? fcn)
(procedure-arity-includes? fcn 1))
(raise-type-error 'build-string "procedure (arity 1)" fcn))
(let ([str (make-string n)])
(let loop ((i 0))
(if (= i n)
str
(begin (string-set! str i (fcn i)) (loop (add1 i)))))))
(define (build-list n fcn)
(unless (exact-nonnegative-integer? n)
(raise-type-error 'build-list "exact-nonnegative-integer" n))
(unless (and (procedure? fcn)
(procedure-arity-includes? fcn 1))
(raise-type-error 'build-list "procedure (arity 1)" fcn))
(let recr ([j 0] [i n])
(cond [(zero? i) null]
[else (cons (fcn j)
(recr (add1 j) (sub1 i)))])))
(define-values [compose1 compose]
(let ()
(define-syntax-rule (app1 E1 E2) (E1 E2))
(define-syntax-rule (app* E1 E2) (call-with-values (lambda () E2) E1))
(define-syntax-rule (mk-simple-compose app f g)
(let*-values
([(arity) (procedure-arity g)]
[(required-kwds allowed-kwds) (values '() '()) (procedure-keywords g)]
[(composed)
(if (eq? 1 arity)
(lambda (x) (app f (g x)))
(case-lambda [(x) (app f (g x))]
[(x y) (app f (g x y))]
[args (app f (apply g args))]))])
composed (if (null? allowed-kwds)
composed
(make-keyword-procedure (lambda (kws kw-args . xs)
(app f (keyword-apply g kws kw-args xs)))
composed))))
(define-syntax-rule (can-compose* name n g f fs)
(unless (null? (let-values ([(req _) (values '() '()) (procedure-keywords g)]) req))
(apply raise-type-error 'name "procedure (no required keywords)"
n f fs)))
(define-syntax-rule (can-compose1 name n g f fs)
(begin (unless (procedure-arity-includes? g 1)
(apply raise-type-error 'name "procedure (arity 1)" n f fs))
(can-compose* name n g f fs)))
(define (pipeline1 f rfuns)
(lambda (x)
(let loop ([x x] [f f] [rfuns rfuns])
(if (null? rfuns)
(f x)
(loop (f x) (car rfuns) (cdr rfuns))))))
(define (pipeline* f rfuns)
(if (eqv? 1 (procedure-arity f))
(let loop ([f f] [rfuns rfuns])
(if (null? rfuns)
f
(loop (let ([fst (car rfuns)])
(if (eqv? 1 (procedure-arity fst))
(lambda (x) (fst (f x)))
(lambda (x) (app* fst (f x)))))
(cdr rfuns))))
(let ([funs (reverse (cons f rfuns))])
(let loop ([f (car funs)] [funs (cdr funs)])
(if (null? funs)
f
(loop (let ([fst (car funs)])
(if (eqv? 1 (procedure-arity f))
(if (eqv? 1 (procedure-arity fst))
(lambda (x) (f (fst x)))
(lambda xs (f (apply fst xs))))
(if (eqv? 1 (procedure-arity fst))
(lambda (x) (app* f (fst x)))
(lambda xs (app* f (apply fst xs))))))
(cdr funs)))))))
(define-syntax-rule (mk name app can-compose pipeline mk-simple-compose)
(define name
(let ([simple-compose mk-simple-compose])
(case-lambda
[(f)
(if (procedure? f) f (raise-type-error 'name "procedure" 0 f))]
[(f g)
(unless (procedure? f)
(raise-type-error 'name "procedure" 0 f g))
(unless (procedure? g)
(raise-type-error 'name "procedure" 1 f g))
(can-compose name 0 f f '())
(simple-compose f g)]
[() values]
[(f0 . fs0)
(let loop ([f f0] [fs fs0] [i 0] [rfuns '()])
(unless (procedure? f)
(apply raise-type-error 'name "procedure" i f0 fs0))
(if (pair? fs)
(begin (can-compose name i f f0 fs0)
(loop (car fs) (cdr fs) (add1 i) (cons f rfuns)))
(simple-compose (pipeline (car rfuns) (cdr rfuns)) f)))]))))
(mk compose1 app1 can-compose1 pipeline1
(lambda (f g) (mk-simple-compose app1 f g)))
(mk compose app* can-compose* pipeline*
(lambda (f g)
(if (eqv? 1 (procedure-arity f))
(mk-simple-compose app1 f g)
(mk-simple-compose app* f g))))
(values compose1 compose)))