pax_global_header 0000666 0000000 0000000 00000000064 14737542645 0014533 g ustar 00root root 0000000 0000000 52 comment=f1d21cf874332b9ffb80f1d36d747e424fe7525d
chez-srfi-0.0+git20241031.b424440+dfsg/ 0000775 0000000 0000000 00000000000 14737542645 0016501 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a0.sls 0000664 0000000 0000000 00000000231 14737542645 0017650 0 ustar 00root root 0000000 0000000 #!r6rs
;; Automatically generated by private/make-aliased-libraries.sps
(library (srfi :0)
(export
cond-expand)
(import (srfi :0 cond-expand))
)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a0/ 0000775 0000000 0000000 00000000000 14737542645 0017131 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a0/cond-expand.guile.sls 0000664 0000000 0000000 00000000125 14737542645 0023156 0 ustar 00root root 0000000 0000000 (library (srfi srfi-0)
(export cond-expand)
(import (only (guile) cond-expand)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a0/cond-expand.sls 0000664 0000000 0000000 00000003017 14737542645 0022055 0 ustar 00root root 0000000 0000000 #!r6rs
;; Copyright 2009 Derick Eddington. My MIT-style license is in the file named
;; LICENSE from the original collection this file is distributed with.
(library (srfi :0 cond-expand)
(export
cond-expand)
(import
(rnrs)
(for (only (srfi private registry) expand-time-features) expand))
(define-syntax cond-expand
(lambda (stx)
(syntax-case stx (and or not else)
((_)
(syntax-violation #F "unfulfilled cond-expand" stx))
((_ (else body ...))
#'(begin body ...))
((_ ((and) body ...) more-clauses ...)
#'(begin body ...))
((_ ((and req1 req2 ...) body ...) more-clauses ...)
#'(cond-expand
(req1
(cond-expand
((and req2 ...) body ...)
more-clauses ...))
more-clauses ...))
((_ ((or) body ...) more-clauses ...)
#'(cond-expand more-clauses ...))
((_ ((or req1 req2 ...) body ...) more-clauses ...)
#'(cond-expand
(req1
(begin body ...))
(else
(cond-expand
((or req2 ...) body ...)
more-clauses ...))))
((_ ((not req) body ...) more-clauses ...)
#'(cond-expand
(req
(cond-expand more-clauses ...))
(else body ...)))
((_ (feature-id body ...) more-clauses ...)
(if (member (syntax->datum #'feature-id) expand-time-features)
#'(begin body ...)
#'(cond-expand more-clauses ...))))))
)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a1.sls 0000664 0000000 0000000 00000003752 14737542645 0017664 0 ustar 00root root 0000000 0000000 #!r6rs
;; Automatically generated by private/make-aliased-libraries.sps
(library (srfi :1)
(export
alist-cons
alist-copy
alist-delete
alist-delete!
any
append
append!
append-map
append-map!
append-reverse
append-reverse!
assoc
assq
assv
break
break!
caaaar
caaadr
caaar
caadar
caaddr
caadr
caar
cadaar
cadadr
cadar
caddar
cadddr
caddr
cadr
car
car+cdr
cdaaar
cdaadr
cdaar
cdadar
cdaddr
cdadr
cdar
cddaar
cddadr
cddar
cdddar
cddddr
cdddr
cddr
cdr
circular-list
circular-list?
concatenate
concatenate!
cons
cons*
count
delete
delete!
delete-duplicates
delete-duplicates!
dotted-list?
drop
drop-right
drop-right!
drop-while
eighth
every
fifth
filter
filter!
filter-map
find
find-tail
first
fold
fold-right
for-each
fourth
iota
last
last-pair
length
length+
list
list-copy
list-index
list-ref
list-tabulate
list=
lset-adjoin
lset-diff+intersection
lset-diff+intersection!
lset-difference
lset-difference!
lset-intersection
lset-intersection!
lset-union
lset-union!
lset-xor
lset-xor!
lset<=
lset=
make-list
map
map!
map-in-order
member
memq
memv
ninth
not-pair?
null-list?
null?
pair-fold
pair-fold-right
pair-for-each
pair?
partition
partition!
proper-list?
reduce
reduce-right
remove
remove!
reverse
reverse!
second
set-car!
set-cdr!
seventh
sixth
span
span!
split-at
split-at!
take
take!
take-right
take-while
take-while!
tenth
third
unfold
unfold-right
unzip1
unzip2
unzip3
unzip4
unzip5
xcons
zip)
(import (srfi :1 lists))
)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a1/ 0000775 0000000 0000000 00000000000 14737542645 0017132 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a1/lists.sls 0000664 0000000 0000000 00000004373 14737542645 0021022 0 ustar 00root root 0000000 0000000 #!r6rs
;; Copyright 2010 Derick Eddington. My MIT-style license is in the file named
;; LICENSE from the original collection this file is distributed with.
(library (srfi :1 lists)
(export
;;; Exported:
xcons #;tree-copy make-list list-tabulate list-copy
proper-list? circular-list? dotted-list? not-pair? null-list? list=
circular-list length+
iota
first second third fourth fifth sixth seventh eighth ninth tenth
car+cdr
take drop
take-right drop-right
take! drop-right!
split-at split-at!
last last-pair
zip unzip1 unzip2 unzip3 unzip4 unzip5
count
append! append-reverse append-reverse! concatenate concatenate!
unfold fold pair-fold reduce
unfold-right pair-fold-right reduce-right
append-map append-map! map! pair-for-each filter-map map-in-order
filter! partition! remove!
find-tail any every list-index
take-while drop-while take-while!
span break span! break!
delete delete!
alist-cons alist-copy
delete-duplicates delete-duplicates!
alist-delete alist-delete!
reverse!
lset<= lset= lset-adjoin
lset-union lset-intersection lset-difference lset-xor
lset-diff+intersection
lset-union! lset-intersection! lset-difference! lset-xor!
lset-diff+intersection!
;; re-exported:
append assq assv caaaar caaadr caaar caadar caaddr
caadr caar cadaar cadadr cadar caddar cadddr caddr cadr
car cdaaar cdaadr cdaar cdadar cdaddr cdadr cdar cddaar
cddadr cddar cdddar cddddr cdddr cddr cdr cons cons*
length list list-ref memq memv null? pair?
reverse set-car! set-cdr!
;; different than R6RS:
assoc filter find fold-right for-each map member partition remove)
(import
(rename (except (rnrs) find filter fold-right map partition remove)
(assoc r6rs:assoc)
(for-each r6rs:for-each)
(member r6rs:member))
(rnrs mutable-pairs)
(srfi :8 receive)
(srfi :23 error tricks)
(for (srfi private vanish) expand)
(srfi private check-arg)
(srfi private include))
(let-syntax ((define (vanish-define define (cons*))))
(SRFI-23-error->R6RS "(library (srfi :1 lists))"
(include/resolve ("srfi" "%3a1") "srfi-1-reference.scm")))
)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a1/srfi-1-reference.scm 0000664 0000000 0000000 00000241457 14737542645 0022710 0 ustar 00root root 0000000 0000000 ;;; SRFI-1 list-processing library -*- Scheme -*-
;;; Reference implementation
;;;
;;; Copyright (c) 1998, 1999 by Olin Shivers. You may do as you please with
;;; this code as long as you do not remove this copyright notice or
;;; hold me liable for its use. Please send bug reports to shivers@ai.mit.edu.
;;; -Olin
;;; Copyright (c) Jéssica Milaré, 2018. You may do as you please with
;;; this code as long as you do not remove this copyright notice or
;;; hold me liable for its use.
;;; This is a library of list- and pair-processing functions. I wrote it after
;;; carefully considering the functions provided by the libraries found in
;;; R4RS/R5RS Scheme, MIT Scheme, Gambit, RScheme, MzScheme, slib, Common
;;; Lisp, Bigloo, guile, T, APL and the SML standard basis. It is a pretty
;;; rich toolkit, providing a superset of the functionality found in any of
;;; the various Schemes I considered.
;;; This implementation is intended as a portable reference implementation
;;; for SRFI-1. See the porting notes below for more information.
;;; Exported:
;;; xcons tree-copy make-list list-tabulate cons* list-copy
;;; proper-list? circular-list? dotted-list? not-pair? null-list? list=
;;; circular-list length+
;;; iota
;;; first second third fourth fifth sixth seventh eighth ninth tenth
;;; car+cdr
;;; take drop
;;; take-right drop-right
;;; take! drop-right!
;;; split-at split-at!
;;; last last-pair
;;; zip unzip1 unzip2 unzip3 unzip4 unzip5
;;; count
;;; append! append-reverse append-reverse! concatenate concatenate!
;;; unfold fold pair-fold reduce
;;; unfold-right fold-right pair-fold-right reduce-right
;;; append-map append-map! map! pair-for-each filter-map map-in-order
;;; filter partition remove
;;; filter! partition! remove!
;;; find find-tail any every list-index
;;; take-while drop-while take-while!
;;; span break span! break!
;;; delete delete!
;;; alist-cons alist-copy
;;; delete-duplicates delete-duplicates!
;;; alist-delete alist-delete!
;;; reverse!
;;; lset<= lset= lset-adjoin
;;; lset-union lset-intersection lset-difference lset-xor lset-diff+intersection
;;; lset-union! lset-intersection! lset-difference! lset-xor! lset-diff+intersection!
;;;
;;; In principle, the following R4RS list- and pair-processing procedures
;;; are also part of this package's exports, although they are not defined
;;; in this file:
;;; Primitives: cons pair? null? car cdr set-car! set-cdr!
;;; Non-primitives: list length append reverse cadr ... cddddr list-ref
;;; memq memv assq assv
;;; (The non-primitives are defined in this file, but commented out.)
;;;
;;; These R4RS procedures have extended definitions in SRFI-1 and are defined
;;; in this file:
;;; map for-each member assoc
;;;
;;; The remaining two R4RS list-processing procedures are not included:
;;; list-tail (use drop)
;;; list? (use proper-list?)
;;; A note on recursion and iteration/reversal:
;;; Many iterative list-processing algorithms naturally compute the elements
;;; of the answer list in the wrong order (left-to-right or head-to-tail) from
;;; the order needed to cons them into the proper answer (right-to-left, or
;;; tail-then-head). One style or idiom of programming these algorithms, then,
;;; loops, consing up the elements in reverse order, then destructively
;;; reverses the list at the end of the loop. I do not do this. The natural
;;; and efficient way to code these algorithms is recursively. This trades off
;;; intermediate temporary list structure for intermediate temporary stack
;;; structure. In a stack-based system, this improves cache locality and
;;; lightens the load on the GC system. Don't stand on your head to iterate!
;;; Recurse, where natural. Multiple-value returns make this even more
;;; convenient, when the recursion/iteration has multiple state values.
;;; Porting:
;;; This is carefully tuned code; do not modify casually.
;;; - It is careful to share storage when possible;
;;; - Side-effecting code tries not to perform redundant writes.
;;;
;;; That said, a port of this library to a specific Scheme system might wish
;;; to tune this code to exploit particulars of the implementation.
;;; The single most important compiler-specific optimisation you could make
;;; to this library would be to add rewrite rules or transforms to:
;;; - transform applications of n-ary procedures (e.g. LIST=, CONS*, APPEND,
;;; LSET-UNION) into multiple applications of a primitive two-argument
;;; variant.
;;; - transform applications of the mapping functions (MAP, FOR-EACH, FOLD,
;;; ANY, EVERY) into open-coded loops. The killer here is that these
;;; functions are n-ary. Handling the general case is quite inefficient,
;;; requiring many intermediate data structures to be allocated and
;;; discarded.
;;; - transform applications of procedures that take optional arguments
;;; into calls to variants that do not take optional arguments. This
;;; eliminates unnecessary consing and parsing of the rest parameter.
;;;
;;; These transforms would provide BIG speedups. In particular, the n-ary
;;; mapping functions are particularly slow and cons-intensive, and are good
;;; candidates for tuning. I have coded fast paths for the single-list cases,
;;; but what you really want to do is exploit the fact that the compiler
;;; usually knows how many arguments are being passed to a particular
;;; application of these functions -- they are usually explicitly called, not
;;; passed around as higher-order values. If you can arrange to have your
;;; compiler produce custom code or custom linkages based on the number of
;;; arguments in the call, you can speed these functions up a *lot*. But this
;;; kind of compiler technology no longer exists in the Scheme world as far as
;;; I can see.
;;;
;;; Note that this code is, of course, dependent upon standard bindings for
;;; the R5RS procedures -- i.e., it assumes that the variable CAR is bound
;;; to the procedure that takes the car of a list. If your Scheme
;;; implementation allows user code to alter the bindings of these procedures
;;; in a manner that would be visible to these definitions, then there might
;;; be trouble. You could consider horrible kludgery along the lines of
;;; (define fact
;;; (let ((= =) (- -) (* *))
;;; (letrec ((real-fact (lambda (n)
;;; (if (= n 0) 1 (* n (real-fact (- n 1)))))))
;;; real-fact)))
;;; Or you could consider shifting to a reasonable Scheme system that, say,
;;; has a module system protecting code from this kind of lossage.
;;;
;;; This code does a fair amount of run-time argument checking. If your
;;; Scheme system has a sophisticated compiler that can eliminate redundant
;;; error checks, this is no problem. However, if not, these checks incur
;;; some performance overhead -- and, in a safe Scheme implementation, they
;;; are in some sense redundant: if we don't check to see that the PROC
;;; parameter is a procedure, we'll find out anyway three lines later when
;;; we try to call the value. It's pretty easy to rip all this argument
;;; checking code out if it's inappropriate for your implementation -- just
;;; nuke every call to CHECK-ARG.
;;;
;;; On the other hand, if you *do* have a sophisticated compiler that will
;;; actually perform soft-typing and eliminate redundant checks (Rice's systems
;;; being the only possible candidate of which I'm aware), leaving these checks
;;; in can *help*, since their presence can be elided in redundant cases,
;;; and in cases where they are needed, performing the checks early, at
;;; procedure entry, can "lift" a check out of a loop.
;;;
;;; Finally, I have only checked the properties that can portably be checked
;;; with R5RS Scheme -- and this is not complete. You may wish to alter
;;; the CHECK-ARG parameter checks to perform extra, implementation-specific
;;; checks, such as procedure arity for higher-order values.
;;;
;;; The code has only these non-R4RS dependencies:
;;; A few calls to an ERROR procedure;
;;; Uses of the R5RS multiple-value procedure VALUES and the m-v binding
;;; RECEIVE macro (which isn't R5RS, but is a trivial macro).
;;; Many calls to a parameter-checking procedure check-arg:
;;; (define (check-arg pred val caller)
;;; (let lp ((val val))
;;; (if (pred val) val (lp (error "Bad argument" val pred caller)))))
;;;
;;; Most of these procedures use the NULL-LIST? test to trigger the
;;; base case in the inner loop or recursion. The NULL-LIST? function
;;; is defined to be a careful one -- it raises an error if passed a
;;; non-nil, non-pair value. The spec allows an implementation to use
;;; a less-careful implementation that simply defines NULL-LIST? to
;;; be NOT-PAIR?. This would speed up the inner loops of these procedures
;;; at the expense of having them silently accept dotted lists.
;;; A note on dotted lists:
;;; I, personally, take the view that the only consistent view of lists
;;; in Scheme is the view that *everything* is a list -- values such as
;;; 3 or "foo" or 'bar are simply empty dotted lists. This is due to the
;;; fact that Scheme actually has no true list type. It has a pair type,
;;; and there is an *interpretation* of the trees built using this type
;;; as lists.
;;;
;;; I lobbied to have these list-processing procedures hew to this
;;; view, and accept any value as a list argument. I was overwhelmingly
;;; overruled during the SRFI discussion phase. So I am inserting this
;;; text in the reference lib and the SRFI spec as a sort of "minority
;;; opinion" dissent.
;;;
;;; Many of the procedures in this library can be trivially redefined
;;; to handle dotted lists, just by changing the NULL-LIST? base-case
;;; check to NOT-PAIR?, meaning that any non-pair value is taken to be
;;; an empty list. For most of these procedures, that's all that is
;;; required.
;;;
;;; However, we have to do a little more work for some procedures that
;;; *produce* lists from other lists. Were we to extend these procedures to
;;; accept dotted lists, we would have to define how they terminate the lists
;;; produced as results when passed a dotted list. I designed a coherent set
;;; of termination rules for these cases; this was posted to the SRFI-1
;;; discussion list. I additionally wrote an earlier version of this library
;;; that implemented that spec. It has been discarded during later phases of
;;; the definition and implementation of this library.
;;;
;;; The argument *against* defining these procedures to work on dotted
;;; lists is that dotted lists are the rare, odd case, and that by
;;; arranging for the procedures to handle them, we lose error checking
;;; in the cases where a dotted list is passed by accident -- e.g., when
;;; the programmer swaps a two arguments to a list-processing function,
;;; one being a scalar and one being a list. For example,
;;; (member '(1 3 5 7 9) 7)
;;; This would quietly return #f if we extended MEMBER to accept dotted
;;; lists.
;;;
;;; The SRFI discussion record contains more discussion on this topic.
;;; Constructors
;;;;;;;;;;;;;;;;
;;; Occasionally useful as a value to be passed to a fold or other
;;; higher-order procedure.
(define (xcons d a) (cons a d))
;;;; Recursively copy every cons.
;(define (tree-copy x)
; (let recur ((x x))
; (if (not (pair? x)) x
; (cons (recur (car x)) (recur (cdr x))))))
;;; Make a list of length LEN.
(define make-list
(case-lambda
((len) (make-list len #f))
((len elt)
(check-arg index? len make-list)
(do ((i len (fx- i 1))
(ans '() (cons elt ans)))
((fx<=? i 0) ans)))))
;(define (list . ans) ans) ; R4RS
;;; Make a list of length LEN. Elt i is (PROC i) for 0 <= i < LEN.
(define (list-tabulate len proc)
(check-arg index? len list-tabulate)
(check-arg procedure? proc list-tabulate)
(do ((i (fx- len 1) (fx- i 1))
(ans '() (cons (proc i) ans)))
((fx i 0) ans)))
;;; (cons* a1 a2 ... an) = (cons a1 (cons a2 (cons ... an)))
;;; (cons* a1) = a1 (cons* a1 a2 ...) = (cons a1 (cons* a2 ...))
;;;
;;; (cons first (unfold not-pair? car cdr rest values))
(define cons*
(case-lambda
((first) first)
((first second) (cons first second))
((first second third . rest)
(cons first
(cons second
(let recur ((x third) (rest rest))
(if (pair? rest)
(cons x (recur (car rest) (cdr rest)))
x)))))))
;;; (unfold not-pair? car cdr lis values)
(define (list-copy lis)
(let recur ((lis lis))
(if (pair? lis)
(cons (car lis) (recur (cdr lis)))
lis)))
;;; IOTA count [start step] (start start+step ... start+(count-1)*step)
(define iota
(case-lambda
((count) (iota count 0 1))
((count start) (iota count start 1))
((count start step)
(check-arg index? count iota)
(check-arg number? start iota)
(check-arg number? step iota)
(let loop ((cur start) (n 0))
(if (fx=? n count)
'()
(cons cur (loop (fx+ cur step) (fx+ 1 n))))))))
;;; I thought these were lovely, but the public at large did not share my
;;; enthusiasm...
;;; :IOTA to (0 ... to-1)
;;; :IOTA from to (from ... to-1)
;;; :IOTA from to step (from from+step ...)
;;; IOTA: to (1 ... to)
;;; IOTA: from to (from+1 ... to)
;;; IOTA: from to step (from+step from+2step ...)
;(define (%parse-iota-args arg1 rest-args proc)
; (let ((check (lambda (n) (check-arg fixnum? n proc))))
; (check arg1)
; (if (pair? rest-args)
; (let ((arg2 (check (car rest-args)))
; (rest (cdr rest-args)))
; (if (pair? rest)
; (let ((arg3 (check (car rest)))
; (rest (cdr rest)))
; (if (pair? rest) (error "Too many parameters" proc arg1 rest-args)
; (values arg1 arg2 arg3)))
; (values arg1 arg2 1)))
; (values 0 arg1 1))))
;
;(define (iota: arg1 . rest-args)
; (receive (from to step) (%parse-iota-args arg1 rest-args iota:)
; (let* ((numsteps (floor (/ (- to from) step)))
; (last-val (+ from (* step numsteps))))
; (if (< numsteps 0) (error "Negative step count" iota: from to step))
; (do ((steps-left numsteps (- steps-left 1))
; (val last-val (- val step))
; (ans '() (cons val ans)))
; ((<= steps-left 0) ans)))))
;
;
;(define (:iota arg1 . rest-args)
; (receive (from to step) (%parse-iota-args arg1 rest-args :iota)
; (let* ((numsteps (ceiling (/ (- to from) step)))
; (last-val (+ from (* step (- numsteps 1)))))
; (if (< numsteps 0) (error "Negative step count" :iota from to step))
; (do ((steps-left numsteps (- steps-left 1))
; (val last-val (- val step))
; (ans '() (cons val ans)))
; ((<= steps-left 0) ans)))))
(define (circular-list val1 . vals)
(let ((ans (cons val1 vals)))
(set-cdr! (last-pair ans) ans)
ans))
;;; ::= () ; Empty proper list
;;; | (cons ) ; Proper-list pair
;;; Note that this definition rules out circular lists -- and this
;;; function is required to detect this case and return false.
(define (proper-list? x)
(let lp ((x x) (lag x))
(if (pair? x)
(let ((x (cdr x)))
(if (pair? x)
(let ((x (cdr x))
(lag (cdr lag)))
(and (not (eq? x lag)) (lp x lag)))
(null? x)))
(null? x))))
;;; A dotted list is a finite list (possibly of length 0) terminated
;;; by a non-nil value. Any non-cons, non-nil value (e.g., "foo" or 5)
;;; is a dotted list of length 0.
;;;
;;; ::= ; Empty dotted list
;;; | (cons ) ; Proper-list pair
(define (dotted-list? x)
(let lp ((x x) (lag x))
(if (pair? x)
(let ((x (cdr x)))
(if (pair? x)
(let ((x (cdr x))
(lag (cdr lag)))
(and (not (eq? x lag)) (lp x lag)))
(not (null? x))))
(not (null? x)))))
(define (circular-list? x)
(let lp ((x x) (lag x))
(and (pair? x)
(let ((x (cdr x)))
(and (pair? x)
(let ((x (cdr x))
(lag (cdr lag)))
(or (eq? x lag) (lp x lag))))))))
(define (not-pair? x) (not (pair? x))) ; Inline me.
;;; This is a legal definition which is fast and sloppy:
;;; (define null-list? not-pair?)
;;; but we'll provide a more careful one:
(define (null-list? l)
(cond ((pair? l) #f)
((null? l) #t)
(else (error "null-list?: argument out of domain" l))))
(define list=
(case-lambda
((elt=) #t)
((elt= list-a) #t)
((elt= list-a list-b)
(or (eq? list-a list-b)
(let loop ((list-a list-a)
(list-b list-b))
(if (null-list? list-a)
(null-list? list-b)
(and (not (null-list? list-b))
(elt= (car list-a) (car list-b))
(loop (cdr list-a) (cdr list-b)))))))
((elt= list-a list-b list-c . lists)
(and (list= elt= list-a list-b)
(list= elt= list-b list-c)
(or (null? lists)
(let loop ((list-a list-c) (lists lists))
(let ((list-b (car lists))
(others (cdr lists)))
(and (list= elt= list-a list-b)
(or (null? others)
(loop list-b others))))))))))
;;; R4RS, so commented out.
;(define (length x) ; LENGTH may diverge or
; (let lp ((x x) (len 0)) ; raise an error if X is
; (if (pair? x) ; a circular list. This version
; (lp (cdr x) (+ len 1)) ; diverges.
; len)))
(define (length+ x) ; Returns #f if X is circular.
;; Try 21 times before checking for cicularities
(let loop ((x x)
(len 0))
(if (null? x)
len
(if (fx>? len 20)
;; Tried 20 times, begin checking for circularities
(let lp ((x (cdr x)) (lag x) (len (fx+ len 1)))
(if (pair? x)
(let ((x (cdr x))
(len (fx+ len 1)))
(if (pair? x)
(let ((x (cdr x))
(lag (cdr lag))
(len (fx+ len 1)))
(and (not (eq? x lag)) (lp x lag len)))
len))
len))
;; Still not 20 times
(loop (cdr x) (fx+ len 1))))))
(define (zip list1 . more-lists) (apply map list list1 more-lists))
;;; Selectors
;;;;;;;;;;;;;
;;; R4RS non-primitives:
;(define (caar x) (car (car x)))
;(define (cadr x) (car (cdr x)))
;(define (cdar x) (cdr (car x)))
;(define (cddr x) (cdr (cdr x)))
;
;(define (caaar x) (caar (car x)))
;(define (caadr x) (caar (cdr x)))
;(define (cadar x) (cadr (car x)))
;(define (caddr x) (cadr (cdr x)))
;(define (cdaar x) (cdar (car x)))
;(define (cdadr x) (cdar (cdr x)))
;(define (cddar x) (cddr (car x)))
;(define (cdddr x) (cddr (cdr x)))
;
;(define (caaaar x) (caaar (car x)))
;(define (caaadr x) (caaar (cdr x)))
;(define (caadar x) (caadr (car x)))
;(define (caaddr x) (caadr (cdr x)))
;(define (cadaar x) (cadar (car x)))
;(define (cadadr x) (cadar (cdr x)))
;(define (caddar x) (caddr (car x)))
;(define (cadddr x) (caddr (cdr x)))
;(define (cdaaar x) (cdaar (car x)))
;(define (cdaadr x) (cdaar (cdr x)))
;(define (cdadar x) (cdadr (car x)))
;(define (cdaddr x) (cdadr (cdr x)))
;(define (cddaar x) (cddar (car x)))
;(define (cddadr x) (cddar (cdr x)))
;(define (cdddar x) (cdddr (car x)))
;(define (cddddr x) (cdddr (cdr x)))
(define first car)
(define second cadr)
(define third caddr)
(define fourth cadddr)
(define (fifth x) (car (cddddr x)))
(define (sixth x) (cadr (cddddr x)))
(define (seventh x) (caddr (cddddr x)))
(define (eighth x) (cadddr (cddddr x)))
(define (ninth x) (car (cddddr (cddddr x))))
(define (tenth x) (cadr (cddddr (cddddr x))))
(define (car+cdr pair) (values (car pair) (cdr pair)))
;;; take & drop
(define (take lis k)
(check-arg fixnum? k take)
(let recur ((lis lis) (k k))
(if (zero? k) '()
(cons (car lis)
(recur (cdr lis) (- k 1))))))
(define (drop lis k)
(check-arg fixnum? k drop)
(let iter ((lis lis) (k k))
(if (zero? k) lis (iter (cdr lis) (- k 1)))))
(define (take! lis k)
(check-arg fixnum? k take!)
(if (zero? k) '()
(begin (set-cdr! (drop lis (- k 1)) '())
lis)))
;;; TAKE-RIGHT and DROP-RIGHT work by getting two pointers into the list,
;;; off by K, then chasing down the list until the lead pointer falls off
;;; the end.
(define (take-right lis k)
(check-arg fixnum? k take-right)
(let lp ((lag lis) (lead (drop lis k)))
(if (pair? lead)
(lp (cdr lag) (cdr lead))
lag)))
(define (drop-right lis k)
(check-arg fixnum? k drop-right)
(let recur ((lag lis) (lead (drop lis k)))
(if (pair? lead)
(cons (car lag) (recur (cdr lag) (cdr lead)))
'())))
;;; In this function, LEAD is actually K+1 ahead of LAG. This lets
;;; us stop LAG one step early, in time to smash its cdr to ().
(define (drop-right! lis k)
(check-arg fixnum? k drop-right!)
(let ((lead (drop lis k)))
(if (pair? lead)
(let lp ((lag lis) (lead (cdr lead))) ; Standard case
(if (pair? lead)
(lp (cdr lag) (cdr lead))
(begin (set-cdr! lag '())
lis)))
'()))) ; Special case dropping everything -- no cons to side-effect.
;(define (list-ref lis i) (car (drop lis i))) ; R4RS
;;; These use the APL convention, whereby negative indices mean
;;; "from the right." I liked them, but they didn't win over the
;;; SRFI reviewers.
;;; K >= 0: Take and drop K elts from the front of the list.
;;; K <= 0: Take and drop -K elts from the end of the list.
;(define (take lis k)
; (check-arg fixnum? k take)
; (if (negative? k)
; (list-tail lis (+ k (length lis)))
; (let recur ((lis lis) (k k))
; (if (zero? k) '()
; (cons (car lis)
; (recur (cdr lis) (- k 1)))))))
;
;(define (drop lis k)
; (check-arg fixnum? k drop)
; (if (negative? k)
; (let recur ((lis lis) (nelts (+ k (length lis))))
; (if (zero? nelts) '()
; (cons (car lis)
; (recur (cdr lis) (- nelts 1)))))
; (list-tail lis k)))
;
;
;(define (take! lis k)
; (check-arg fixnum? k take!)
; (cond ((zero? k) '())
; ((positive? k)
; (set-cdr! (list-tail lis (- k 1)) '())
; lis)
; (else (list-tail lis (+ k (length lis))))))
;
;(define (drop! lis k)
; (check-arg fixnum? k drop!)
; (if (negative? k)
; (let ((nelts (+ k (length lis))))
; (if (zero? nelts) '()
; (begin (set-cdr! (list-tail lis (- nelts 1)) '())
; lis)))
; (list-tail lis k)))
(define (split-at x k)
(check-arg fixnum? k split-at)
(let recur ((lis x) (k k))
(if (zero? k) (values '() lis)
(receive (prefix suffix) (recur (cdr lis) (- k 1))
(values (cons (car lis) prefix) suffix)))))
(define (split-at! x k)
(check-arg fixnum? k split-at!)
(if (zero? k) (values '() x)
(let* ((prev (drop x (- k 1)))
(suffix (cdr prev)))
(set-cdr! prev '())
(values x suffix))))
(define (last lis) (car (last-pair lis)))
(define (last-pair lis)
(check-arg pair? lis last-pair)
(let lp ((lis lis))
(let ((tail (cdr lis)))
(if (pair? tail) (lp tail) lis))))
;;; Unzippers -- 1 through 5
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (unzip1 lis) (map car lis))
(define (unzip2 lis)
(let recur ((lis lis))
(if (null-list? lis) (values lis lis) ; Use NOT-PAIR? to handle
(let ((elt (car lis))) ; dotted lists.
(receive (a b) (recur (cdr lis))
(values (cons (car elt) a)
(cons (cadr elt) b)))))))
(define (unzip3 lis)
(let recur ((lis lis))
(if (null-list? lis) (values lis lis lis)
(let ((elt (car lis)))
(receive (a b c) (recur (cdr lis))
(values (cons (car elt) a)
(cons (cadr elt) b)
(cons (caddr elt) c)))))))
(define (unzip4 lis)
(let recur ((lis lis))
(if (null-list? lis) (values lis lis lis lis)
(let ((elt (car lis)))
(receive (a b c d) (recur (cdr lis))
(values (cons (car elt) a)
(cons (cadr elt) b)
(cons (caddr elt) c)
(cons (cadddr elt) d)))))))
(define (unzip5 lis)
(let recur ((lis lis))
(if (null-list? lis) (values lis lis lis lis lis)
(let ((elt (car lis)))
(receive (a b c d e) (recur (cdr lis))
(values (cons (car elt) a)
(cons (cadr elt) b)
(cons (caddr elt) c)
(cons (cadddr elt) d)
(cons (car (cddddr elt)) e)))))))
;;; append! append-reverse append-reverse! concatenate concatenate!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define append!
(case-lambda
(() '())
;; Fast path 1
((lis1) lis1)
;; Fast path 2
((lis1 lis2)
(cond
((null? lis2) lis1)
((null? lis1) lis2)
(else
(set-cdr! (last-pair lis1) lis2)
lis1)))
;; N-ary case
((lis1 lis2 lis3 . lists)
(let ((append-2! (lambda (lis1 lis2)
(set-cdr! (last-pair lis1) lis2)
lis1))
(lists (delete '() lists)))
(if (null? lists)
(if (null? lis3)
(append! lis1 lis2)
(let* ((lis (if (null? lis2) lis3 (append-2! lis2 lis3)))
(lis (if (null? lis1) lis (append-2! lis1 lis))))
lis))
(let* ((lis (let loop ((lis (car lists))
(lists (cdr lists)))
(if (null? lists) lis
(append-2! lis (loop (car lists) (cdr lists))))))
(lis (if (null? lis3) lis (append-2! lis3 lis)))
(lis (if (null? lis2) lis (append-2! lis2 lis)))
(lis (if (null? lis1) lis (append-2! lis1 lis))))
lis))))))
;;; APPEND is R4RS.
;(define (append . lists)
; (if (pair? lists)
; (let recur ((list1 (car lists)) (lists (cdr lists)))
; (if (pair? lists)
; (let ((tail (recur (car lists) (cdr lists))))
; (fold-right cons tail list1)) ; Append LIST1 & TAIL.
; list1))
; '()))
;(define (append-reverse rev-head tail) (fold cons tail rev-head))
;(define (append-reverse! rev-head tail)
; (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair)
; tail
; rev-head))
;;; Hand-inline the FOLD and PAIR-FOLD ops for speed.
(define (append-reverse rev-head tail)
(let lp ((rev-head rev-head) (tail tail))
(if (null-list? rev-head) tail
(lp (cdr rev-head) (cons (car rev-head) tail)))))
(define (append-reverse! rev-head tail)
(let lp ((rev-head rev-head) (tail tail))
(if (null-list? rev-head) tail
(let ((next-rev (cdr rev-head)))
(set-cdr! rev-head tail)
(lp next-rev rev-head)))))
(define (concatenate lists) (reduce-right append '() lists))
(define (concatenate! lists) (reduce-right append! '() lists))
;;; Fold/map internal utilities
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; These little internal utilities are used by the general
;;; fold & mapper funs for the n-ary cases . It'd be nice if they got inlined.
;;; One the other hand, the n-ary cases are painfully inefficient as it is.
;;; An aggressive implementation should simply re-write these functions
;;; for raw efficiency; I have written them for as much clarity, portability,
;;; and simplicity as can be achieved.
;;;
;;; These functions have funky definitions that are precisely tuned to
;;; the needs of the fold/map procs -- for example, to minimize the number
;;; of times the argument lists need to be examined.
;;; Return (map cdr lists).
;;; However, if any element of LISTS is empty, just abort and return '().
(define (%cdrs lists)
(let f ((ls lists))
(if (pair? ls)
(let ((x (car ls)))
(if (null? x)
'()
(cons (cdr x) (f (cdr ls)))))
'())))
(define (%cars+ lists last-elt) ; (append! (map car lists) (list last-elt))
(let recur ((lists lists))
(if (pair? lists) (cons (caar lists) (recur (cdr lists))) (list last-elt))))
;;; LISTS is a (not very long) non-empty list of lists.
;;; Return two lists: the cars & the cdrs of the lists.
;;; However, if any of the lists is empty, just abort and return [() ()].
(define (%cars+cdrs lists)
(let f ((ls lists))
(if (pair? ls)
(let ((x (car ls)))
(if (null-list? x)
(values '() '())
(receive (cars cdrs) (f (cdr ls))
(values (cons (car x) cars)
(cons (cdr x) cdrs)))))
(values '() '()))))
;;; Like %CARS+CDRS, but we pass in a final elt tacked onto the end of the
;;; cars list. What a hack.
(define (%cars+cdrs+ lists cars-final)
(let f ((ls lists))
(if (pair? ls)
(let ((x (car ls)))
(if (null-list? x)
(values '() '())
(receive (cars cdrs) (f (cdr ls))
(values (cons (car x) cars)
(cons (cdr x) cdrs)))))
(values (list cars-final) '()))))
;;; Like %CARS+CDRS, but blow up if any list is empty.
(define (%cars+cdrs/no-test lists)
(let recur ((lists lists))
(if (pair? lists)
(receive (list other-lists) (car+cdr lists)
(receive (a d) (car+cdr list)
(receive (cars cdrs) (recur other-lists)
(values (cons a cars) (cons d cdrs)))))
(values '() '()))))
;;; count
;;;;;;;;;
(define count
(case-lambda
;; Fast path
((pred list1)
(check-arg procedure? pred count)
(let lp ((lis list1) (i 0))
(if (null-list? lis) i
(lp (cdr lis) (if (pred (car lis)) (fx+ i 1) i)))))
;; N-ary case
((pred list1 . lists)
(check-arg procedure? pred count)
(let lp ((list1 list1) (lists lists) (i 0))
(if (null-list? list1) i
(receive (as ds) (%cars+cdrs lists)
(if (null? as) i
(lp (cdr list1) ds
(if (apply pred (car list1) as) (fx+ i 1) i)))))))))
;;; fold/unfold
;;;;;;;;;;;;;;;
(define unfold-right
(case-lambda
((p f g seed)
(unfold-right p f g seed '()))
((p f g seed tail)
(check-arg procedure? p unfold-right)
(check-arg procedure? f unfold-right)
(check-arg procedure? g unfold-right)
(let lp ((seed seed) (ans tail))
(if (p seed) ans
(lp (g seed)
(cons (f seed) ans)))))))
(define unfold
(case-lambda
((p f g seed)
(check-arg procedure? p unfold)
(check-arg procedure? f unfold)
(check-arg procedure? g unfold)
(let recur ((seed seed))
(if (p seed) '()
(cons (f seed) (recur (g seed))))))
((p f g seed tail-gen)
(check-arg procedure? p unfold)
(check-arg procedure? f unfold)
(check-arg procedure? g unfold)
(let recur ((seed seed))
(if (p seed) (tail-gen seed)
(cons (f seed) (recur (g seed))))))))
(define fold
(case-lambda
;; Fast path 1
((kons knil lis1)
(check-arg procedure? kons fold)
(let lp ((lis lis1) (ans knil))
(if (null-list? lis) ans
(lp (cdr lis) (kons (car lis) ans)))))
;; Fast path 2
((kons knil lis1 lis2)
(check-arg procedure? kons fold)
(let lp ((lis1 lis1) (lis2 lis2) (ans knil))
(if (or (null-list? lis1) (null-list? lis2))
ans
(lp (cdr lis1) (cdr lis2) (kons (car lis1) (car lis2) ans)))))
;; N-ary case
((kons knil . lists)
(check-arg procedure? kons fold)
(let lp ((lists lists) (ans knil)) ; N-ary case
(receive (cars+ans cdrs) (%cars+cdrs+ lists ans)
(if (null? cars+ans) ans ; Done.
(lp cdrs (apply kons cars+ans))))))))
(define fold-right
(case-lambda
;; Fast path 1
((kons knil lis1)
(check-arg procedure? kons fold-right)
(let recur ((lis lis1))
(if (null-list? lis) knil
(kons (car lis) (recur (cdr lis))))))
;; Fast path 2
((kons knil lis1 lis2)
(check-arg procedure? kons fold-right)
(let recur ((lis1 lis1) (lis2 lis2))
(if (or (null-list? lis1) (null-list? lis2))
knil
(kons (car lis1) (car lis2) (recur (cdr lis1) (cdr lis2))))))
;; N-ary case
((kons knil . lists)
(check-arg procedure? kons fold-right)
(let recur ((lists lists))
(let ((cdrs (%cdrs lists)))
(if (null? cdrs) knil
(apply kons (%cars+ lists (recur cdrs)))))))))
(define pair-fold-right
(case-lambda
;; Fast path
((f zero lis1)
(check-arg procedure? f pair-fold-right)
(let recur ((lis lis1))
(if (null-list? lis) zero (f lis (recur (cdr lis))))))
;; N-ary case
((f zero lis1 lis2 . lists)
(check-arg procedure? f pair-fold-right)
(let recur ((lis1 lis1) (lis2 lis2)
(lists lists))
(if (or (null-list? lis1) (null-list? lis2))
zero
(let ((cdrs (%cdrs lists)))
(if (null? cdrs) zero
(apply f lis1 lis2
(append! lists (list (recur (cdr lis1) (cdr lis2) cdrs)))))))))))
(define pair-fold
(case-lambda
;; Fast path
((f zero lis1)
(check-arg procedure? f pair-fold)
(let lp ((lis lis1) (ans zero))
(if (null? lis) ans
(let ((tail (cdr lis))) ; Grab the cdr now,
(lp tail (f lis ans))))))
;; N-ary case
((f zero lis1 lis2 . lists)
(check-arg procedure? f pair-fold)
(let lp ((lis1 lis1) (lis2 lis2)
(lists lists) (ans zero))
(if (or (null-list? lis1) (null-list? lis2))
ans
(let ((tails (%cdrs lists)))
(if (null? tails) ans
(lp (cdr lis1) (cdr lis2)
tails (apply f lis1 lis2 (append! lists (list ans)))))))))))
;;; REDUCE and REDUCE-RIGHT only use RIDENTITY in the empty-list case.
;;; These cannot meaningfully be n-ary.
(define (reduce f ridentity lis)
(check-arg procedure? f reduce)
(if (null-list? lis) ridentity
(fold f (car lis) (cdr lis))))
(define (reduce-right f ridentity lis)
(check-arg procedure? f reduce-right)
(if (null-list? lis) ridentity
(let recur ((head (car lis)) (lis (cdr lis)))
(if (pair? lis)
(f head (recur (car lis) (cdr lis)))
head))))
;;; Mappers: append-map append-map! pair-for-each map! filter-map map-in-order
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define append-map
(case-lambda
((f lis1)
(really-append-map append-map append f lis1))
((f lis1 lis2)
(really-append-map append-map append f lis1 lis2))
((f lis1 lis2 . lists)
(really-append-map append-map append f lis1 lis2 lists))))
(define append-map!
(case-lambda
((f lis1)
(really-append-map append-map! append! f lis1))
((f lis1 lis2)
(really-append-map append-map! append! f lis1 lis2))
((f lis1 lis2 . lists)
(really-append-map append-map! append! f lis1 lis2 lists))))
(define really-append-map
(case-lambda
;; Fast path 1
((who appender f lis1)
(check-arg procedure? f who)
(if (null-list? lis1) '()
(let recur ((elt (car lis1)) (rest (cdr lis1)))
(let ((vals (f elt)))
(if (null-list? rest) vals
(appender vals (recur (car rest) (cdr rest))))))))
;; Fast path 2
((who appender f lis1 lis2)
(check-arg procedure? f who)
(if (or (null-list? lis1) (null-list? lis2))
'()
(let recur ((lis1 lis1) (lis2 lis2))
(let ((vals (f (car lis1) (car lis2)))
(lis1 (cdr lis1)) (lis2 (cdr lis2)))
(if (or (null-list? lis1) (null-list? lis2))
vals
(appender vals (recur lis1 lis2)))))))
;; N-ary case
((who appender f lis1 lis2 lists)
(check-arg procedure? f who)
(if (or (null-list? lis1) (null-list? lis2))
'()
(receive (cars cdrs) (%cars+cdrs lists)
(if (null? cars) '()
(let recur ((lis1 lis1) (lis2 lis2)
(cars cars) (cdrs cdrs))
(let ((vals (apply f (car lis1) (car lis2) cars))
(lis1 (cdr lis1)) (lis2 (cdr lis2)))
(if (or (null-list? lis1) (null-list? lis2))
vals
(receive (cars2 cdrs2) (%cars+cdrs cdrs)
(if (null? cars2) vals
(appender vals (recur lis1 lis2 cars2 cdrs2)))))))))))))
(define pair-for-each
(case-lambda
;; Fast path
((proc lis1)
(check-arg procedure? proc pair-for-each)
(let lp ((lis lis1))
(if (not (null-list? lis))
(let ((tail (cdr lis))) ; Grab the cdr now,
(proc lis) ; in case PROC SET-CDR!s LIS.
(lp tail)))))
;; N-ary case
((proc lis1 lis2 . lists)
(check-arg procedure? proc pair-for-each)
(let lp ((lis1 lis1) (lis2 lis2)
(lists lists))
(if (and (pair? lis1) (pair? lis2))
(let ((tails (%cdrs lists)))
(if (pair? tails)
(begin (apply proc lis1 lis2 lists)
(lp (cdr lis1) (cdr lis2) tails)))))))))
;;; We stop when LIS1 runs out, not when any list runs out.
(define map!
(case-lambda
;; Fast path 1
((f lis1)
(check-arg procedure? f map!)
(let lp ((lis1 lis1))
(when (not (null-list? lis1))
(set-car! lis1 (f (car lis1)))
(lp (cdr lis1))))
lis1)
;; Fast path 2
((f lis1 lis2)
(check-arg procedure? f map!)
(let lp ((lis1 lis1) (lis2 lis2))
(when (not (null-list? lis1))
(set-car! lis1 (f (car lis1) (car lis2)))
(lp (cdr lis1) (cdr lis2))))
lis1)
;; N-ary case
((f lis1 lis2 lis3 . lists)
(check-arg procedure? f map!)
(let lp ((lis1 lis1) (lis2 lis2) (lis3 lis3) (lists lists))
(when (not (null-list? lis1))
(receive (heads tails) (%cars+cdrs/no-test lists)
(set-car! lis1 (apply f (car lis1) (car lis2) (car lis3) heads))
(lp (cdr lis1) (cdr lis2) (cdr lis3) tails))))
lis1)))
;;; Map F across L, and save up all the non-false results.
(define filter-map
(case-lambda
((f lis1)
(check-arg procedure? f filter-map)
(let recur ((lis lis1))
(if (null-list? lis) '()
(let ((tail (recur (cdr lis))))
(cond ((f (car lis)) => (lambda (x) (cons x tail)))
(else tail))))))
((f lis1 . lists)
(check-arg procedure? f filter-map)
(let recur ((lis1 lis1) (lists lists))
(if (null-list? lis1) '()
(receive (cars cdrs) (%cars+cdrs lists)
(if (pair? cars)
(cond ((apply f (car lis1) cars)
=>
(lambda (x) (cons x (recur (cdr lis1) cdrs))))
(else (recur (cdr lis1) cdrs))) ; Tail call in this arm.
'())))))))
;;; Map F across lists, guaranteeing to go left-to-right.
;;; NOTE: Some implementations of R5RS MAP are compliant with this spec;
;;; in which case this procedure may simply be defined as a synonym for MAP.
(define map-in-order
(case-lambda
((f lis1)
(check-arg procedure? f map-in-order)
(let recur ((lis lis1))
(if (null-list? lis)
lis
(let ((tail (cdr lis))
(x (f (car lis)))) ; Do head first,
(cons x (recur tail)))))) ; then tail
((f lis1 lis2)
(check-arg procedure? f map-in-order)
(let recur ((lis1 lis1) (lis2 lis2))
(if (and (pair? lis1) (pair? lis2))
(let ((x (f (car lis1) (car lis2)))) ; Do head first,
(cons x (recur (cdr lis1) (cdr lis2)))) ; then tail.
'())))
((f lis1 lis2 . lists)
(check-arg procedure? f map-in-order)
(let recur ((lis1 lis1) (lis2 lis2) (lists lists))
(receive (cars cdrs) (%cars+cdrs lists)
(if (and (pair? lis1) (pair? lis2) (pair? cars))
(let ((x (apply f (car lis1) (car lis2) cars))) ; Do head first,
(cons x (recur (cdr lis1) (cdr lis2) cdrs))) ; then tail.
'()))))))
;;; We extend MAP to handle arguments of unequal length.
(define map map-in-order)
;;; Contributed by Michael Sperber since it was missing from the
;;; reference implementation.
(define for-each
(case-lambda
;; Fast path 1
((f lis1)
(r6rs:for-each f lis1))
;; Fast path 2
((f lis1 lis2)
(check-arg procedure? f for-each)
(let recur ((lis1 lis1) (lis2 lis2))
(unless (or (null-list? lis1) (null-list? lis2))
(f (car lis1) (car lis2))
(recur (cdr lis1) (cdr lis2)))))
;; N-ary case
((f lis1 lis2 . lists)
(check-arg procedure? f for-each)
(let recur ((lis1 lis1) (lis2 lis2) (lists lists))
(unless (or (null-list? lis1) (null-list? lis2))
(receive (cars cdrs) (%cars+cdrs lists)
(when (pair? cars)
(apply f (car lis1) (car lis2) cars) ; Do head first,
(recur (cdr lis1) (cdr lis2) cdrs)))))))) ; then tail.
;;; filter, remove, partition
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; FILTER, REMOVE, PARTITION and their destructive counterparts do not
;;; disorder the elements of their argument.
;; This FILTER shares the longest tail of L that has no deleted elements.
;; If Scheme had multi-continuation calls, they could be made more efficient.
(define (filter pred lis) ; Sleazing with EQ? makes this
(check-arg procedure? pred filter) ; one faster.
(let recur ((lis lis))
(if (null-list? lis) lis ; Use NOT-PAIR? to handle dotted lists.
(let ((head (car lis))
(tail (cdr lis)))
(if (pred head)
(let ((new-tail (recur tail))) ; Replicate the RECUR call so
(if (eq? tail new-tail) lis
(cons head new-tail)))
(recur tail)))))) ; this one can be a tail call.
;;; Another version that shares longest tail.
;(define (filter pred lis)
; (receive (ans no-del?)
; ;; (recur l) returns L with (pred x) values filtered.
; ;; It also returns a flag NO-DEL? if the returned value
; ;; is EQ? to L, i.e. if it didn't have to delete anything.
; (let recur ((l l))
; (if (null-list? l) (values l #t)
; (let ((x (car l))
; (tl (cdr l)))
; (if (pred x)
; (receive (ans no-del?) (recur tl)
; (if no-del?
; (values l #t)
; (values (cons x ans) #f)))
; (receive (ans no-del?) (recur tl) ; Delete X.
; (values ans #f))))))
; ans))
;(define (filter! pred lis) ; Things are much simpler
; (let recur ((lis lis)) ; if you are willing to
; (if (pair? lis) ; push N stack frames & do N
; (cond ((pred (car lis)) ; SET-CDR! writes, where N is
; (set-cdr! lis (recur (cdr lis))); the length of the answer.
; lis)
; (else (recur (cdr lis))))
; lis)))
;;; This implementation of FILTER!
;;; - doesn't cons, and uses no stack;
;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are
;;; usually expensive on modern machines, and can be extremely expensive on
;;; modern Schemes (e.g., ones that have generational GC's).
;;; It just zips down contiguous runs of in and out elts in LIS doing the
;;; minimal number of SET-CDR!s to splice the tail of one run of ins to the
;;; beginning of the next.
(define (filter! pred lis)
(check-arg procedure? pred filter!)
(let lp ((ans lis))
(cond ((null-list? ans) ans) ; Scan looking for
((not (pred (car ans))) (lp (cdr ans))) ; first cons of result.
;; ANS is the eventual answer.
;; SCAN-IN: (CDR PREV) = LIS and (CAR PREV) satisfies PRED.
;; Scan over a contiguous segment of the list that
;; satisfies PRED.
;; SCAN-OUT: (CAR PREV) satisfies PRED. Scan over a contiguous
;; segment of the list that *doesn't* satisfy PRED.
;; When the segment ends, patch in a link from PREV
;; to the start of the next good segment, and jump to
;; SCAN-IN.
(else (letrec ((scan-in (lambda (prev lis)
(if (pair? lis)
(if (pred (car lis))
(scan-in lis (cdr lis))
(scan-out prev (cdr lis))))))
(scan-out (lambda (prev lis)
(let lp ((lis lis))
(if (pair? lis)
(if (pred (car lis))
(begin (set-cdr! prev lis)
(scan-in lis (cdr lis)))
(lp (cdr lis)))
(set-cdr! prev lis))))))
(scan-in ans (cdr ans))
ans)))))
;;; Answers share common tail with LIS where possible;
;;; the technique is slightly subtle.
(define (partition pred lis)
(check-arg procedure? pred partition)
(let recur ((lis lis))
(if (null-list? lis) (values lis lis) ; Use NOT-PAIR? to handle dotted lists.
(let ((elt (car lis))
(tail (cdr lis)))
(receive (in out) (recur tail)
(if (pred elt)
(values (if (pair? out) (cons elt in) lis) out)
(values in (if (pair? in) (cons elt out) lis))))))))
;(define (partition! pred lis) ; Things are much simpler
; (let recur ((lis lis)) ; if you are willing to
; (if (null-list? lis) (values lis lis) ; push N stack frames & do N
; (let ((elt (car lis))) ; SET-CDR! writes, where N is
; (receive (in out) (recur (cdr lis)) ; the length of LIS.
; (cond ((pred elt)
; (set-cdr! lis in)
; (values lis out))
; (else (set-cdr! lis out)
; (values in lis))))))))
;;; This implementation of PARTITION!
;;; - doesn't cons, and uses no stack;
;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are
;;; usually expensive on modern machines, and can be extremely expensive on
;;; modern Schemes (e.g., ones that have generational GC's).
;;; It just zips down contiguous runs of in and out elts in LIS doing the
;;; minimal number of SET-CDR!s to splice these runs together into the result
;;; lists.
(define (partition! pred lis)
(check-arg procedure? pred partition!)
(if (null-list? lis) (values lis lis)
;; This pair of loops zips down contiguous in & out runs of the
;; list, splicing the runs together. The invariants are
;; SCAN-IN: (cdr in-prev) = LIS.
;; SCAN-OUT: (cdr out-prev) = LIS.
(letrec ((scan-in (lambda (in-prev out-prev lis)
(let lp ((in-prev in-prev) (lis lis))
(if (pair? lis)
(if (pred (car lis))
(lp lis (cdr lis))
(begin (set-cdr! out-prev lis)
(scan-out in-prev lis (cdr lis))))
(set-cdr! out-prev lis))))) ; Done.
(scan-out (lambda (in-prev out-prev lis)
(let lp ((out-prev out-prev) (lis lis))
(if (pair? lis)
(if (pred (car lis))
(begin (set-cdr! in-prev lis)
(scan-in lis out-prev (cdr lis)))
(lp lis (cdr lis)))
(set-cdr! in-prev lis)))))) ; Done.
;; Crank up the scan&splice loops.
(if (pred (car lis))
;; LIS begins in-list. Search for out-list's first pair.
(let lp ((prev-l lis) (l (cdr lis)))
(cond ((not (pair? l)) (values lis l))
((pred (car l)) (lp l (cdr l)))
(else (scan-out prev-l l (cdr l))
(values lis l)))) ; Done.
;; LIS begins out-list. Search for in-list's first pair.
(let lp ((prev-l lis) (l (cdr lis)))
(cond ((not (pair? l)) (values l lis))
((pred (car l))
(scan-in l prev-l (cdr l))
(values l lis)) ; Done.
(else (lp l (cdr l)))))))))
;; (define (remove pred l) (filter (lambda (x) (not (pred x))) l))
;; (define (remove! pred l) (filter! (lambda (x) (not (pred x))) l))
;; Avoid allocating a procedure
;; Just a copy of filter with (pred head) <-> (not (pred head))
(define (remove pred lis)
(check-arg procedure? pred remove)
(let recur ((lis lis))
(if (null-list? lis) lis
(let ((head (car lis))
(tail (cdr lis)))
(if (not (pred head))
(let ((new-tail (recur tail)))
(if (eq? tail new-tail) lis
(cons head new-tail)))
(recur tail))))))
;; Avoid allocating a procedure
;; Just a copy of filter! with (pred head) <-> (not (pred head))
(define (remove! pred lis)
(check-arg procedure? pred remove!)
(let lp ((ans lis))
(cond ((null-list? ans) ans) ; Scan looking for
((pred (car ans)) (lp (cdr ans))) ; first cons of result.
(else (letrec ((scan-in (lambda (prev lis)
(if (pair? lis)
(if (not (pred (car lis)))
(scan-in lis (cdr lis))
(scan-out prev (cdr lis))))))
(scan-out (lambda (prev lis)
(let lp ((lis lis))
(if (pair? lis)
(if (not (pred (car lis)))
(begin (set-cdr! prev lis)
(scan-in lis (cdr lis)))
(lp (cdr lis)))
(set-cdr! prev lis))))))
(scan-in ans (cdr ans))
ans)))))
;;; Here's the taxonomy for the DELETE/ASSOC/MEMBER functions.
;;; (I don't actually think these are the world's most important
;;; functions -- the procedural FILTER/REMOVE/FIND/FIND-TAIL variants
;;; are far more general.)
;;;
;;; Function Action
;;; ---------------------------------------------------------------------------
;;; remove pred lis Delete by general predicate
;;; delete x lis [=] Delete by element comparison
;;;
;;; find pred lis Search by general predicate
;;; find-tail pred lis Search by general predicate
;;; member x lis [=] Search by element comparison
;;;
;;; assoc key lis [=] Search alist by key comparison
;;; alist-delete key alist [=] Alist-delete by key comparison
(define delete
(case-lambda
((x lis)
(delete x lis equal?))
((x lis elt=)
(let recur ((lis lis))
(if (null-list? lis) lis
(let ((head (car lis))
(tail (cdr lis)))
(if (not (elt= x head))
(let ((new-tail (recur tail)))
(if (eq? tail new-tail) lis
(cons head new-tail)))
(recur tail))))))))
(define delete!
(case-lambda
((x lis)
(delete! x lis equal?))
((x lis elt=)
(let lp ((ans lis))
(cond ((null-list? ans) ans) ; Scan looking for
((elt= x (car ans)) (lp (cdr ans))) ; first cons of result.
(else (letrec ((scan-in (lambda (prev lis)
(if (pair? lis)
(if (not (elt= x (car lis)))
(scan-in lis (cdr lis))
(scan-out prev (cdr lis))))))
(scan-out (lambda (prev lis)
(let lp ((lis lis))
(if (pair? lis)
(if (not (elt= x (car lis)))
(begin (set-cdr! prev lis)
(scan-in lis (cdr lis)))
(lp (cdr lis)))
(set-cdr! prev lis))))))
(scan-in ans (cdr ans))
ans)))))))
;;; Extended from R4RS to take an optional comparison argument.
(define member
(case-lambda
((x lis)
(r6rs:member x lis))
((x lis elt=)
(let lp ((lis lis))
(and (not (null-list? lis))
(if (elt= x (car lis)) lis
(lp (cdr lis))))))))
;;; R4RS, hence we don't bother to define.
;;; The MEMBER and then FIND-TAIL call should definitely
;;; be inlined for MEMQ & MEMV.
;(define (memq x lis) (member x lis eq?))
;(define (memv x lis) (member x lis eqv?))
;;; right-duplicate deletion
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; delete-duplicates delete-duplicates!
;;;
;;; Beware -- these are N^2 algorithms. To efficiently remove duplicates
;;; in long lists, sort the list to bring duplicates together, then use a
;;; linear-time algorithm to kill the dups. Or use an algorithm based on
;;; element-marking. The former gives you O(n lg n), the latter is linear.
(define delete-duplicates
(case-lambda
((lis)
(delete-duplicates lis equal?))
((lis elt=)
(check-arg procedure? elt= delete-duplicates)
(let recur ((lis lis))
(if (null-list? lis) lis
(let* ((x (car lis))
(tail (cdr lis))
(new-tail (recur (delete x tail elt=))))
(if (eq? tail new-tail) lis (cons x new-tail))))))))
(define delete-duplicates!
(case-lambda
((lis)
(delete-duplicates! lis equal?))
((lis elt=)
(check-arg procedure? elt= delete-duplicates!)
(let recur ((lis lis))
(if (null-list? lis) lis
(let* ((x (car lis))
(tail (cdr lis))
(new-tail (recur (delete! x tail elt=))))
(if (not (eq? tail new-tail))
(set-cdr! lis new-tail))
lis))))))
;;; alist stuff
;;;;;;;;;;;;;;;
;;; Extended from R4RS to take an optional comparison argument.
(define assoc
(case-lambda
((x lis)
(r6rs:assoc x lis))
((x lis elt=)
(let loop ((lis lis))
(if (pair? lis)
(let ((entry (car lis)))
(if (elt= x (car entry)) entry
(loop (cdr lis))))
#f)))))
(define (alist-cons key datum alist) (cons (cons key datum) alist))
(define (alist-copy alist)
(map (lambda (elt) (cons (car elt) (cdr elt)))
alist))
(define alist-delete
(case-lambda
((key alist)
(remove (lambda (elt) (equal? key (car elt))) alist))
((key alist elt=)
(remove (lambda (elt) (elt= key (car elt))) alist))))
(define alist-delete!
(case-lambda
((key alist)
(remove! (lambda (elt) (equal? key (car elt))) alist))
((key alist elt=)
(remove! (lambda (elt) (elt= key (car elt))) alist))))
;;; find find-tail take-while drop-while span break any every list-index
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (find pred lis)
(check-arg procedure? pred find)
(let loop ((lis lis))
(if (pair? lis)
(let ((head (car lis)))
(if (pred head) head
(loop (cdr lis))))
#f)))
(define (find-tail pred lis)
(check-arg procedure? pred find-tail)
(let lp ((lis lis))
(and (not (null-list? lis))
(if (pred (car lis)) lis
(lp (cdr lis))))))
(define (take-while pred lis)
(check-arg procedure? pred take-while)
(let recur ((lis lis))
(if (null-list? lis) '()
(let ((x (car lis)))
(if (pred x)
(cons x (recur (cdr lis)))
'())))))
(define (drop-while pred lis)
(check-arg procedure? pred drop-while)
(let lp ((lis lis))
(if (null-list? lis) '()
(if (pred (car lis))
(lp (cdr lis))
lis))))
(define (take-while! pred lis)
(check-arg procedure? pred take-while!)
(if (or (null-list? lis) (not (pred (car lis)))) '()
(begin (let lp ((prev lis) (rest (cdr lis)))
(if (pair? rest)
(let ((x (car rest)))
(if (pred x) (lp rest (cdr rest))
(set-cdr! prev '())))))
lis)))
(define (span pred lis)
(check-arg procedure? pred span)
(let recur ((lis lis))
(if (null-list? lis) (values '() '())
(let ((x (car lis)))
(if (pred x)
(receive (prefix suffix) (recur (cdr lis))
(values (cons x prefix) suffix))
(values '() lis))))))
(define (span! pred lis)
(check-arg procedure? pred span!)
(if (or (null-list? lis) (not (pred (car lis)))) (values '() lis)
(let ((suffix (let lp ((prev lis) (rest (cdr lis)))
(if (null-list? rest) rest
(let ((x (car rest)))
(if (pred x) (lp rest (cdr rest))
(begin (set-cdr! prev '())
rest)))))))
(values lis suffix))))
;; (define (break pred lis) (span (lambda (x) (not (pred x))) lis))
;; (define (break! pred lis) (span! (lambda (x) (not (pred x))) lis))
;; span with pred -> not pred
(define (break pred lis)
(check-arg procedure? pred break)
(let recur ((lis lis))
(if (null-list? lis) (values '() '())
(let ((x (car lis)))
(if (not (pred x))
(receive (prefix suffix) (recur (cdr lis))
(values (cons x prefix) suffix))
(values '() lis))))))
;; span! with pred <-> not pred
(define (break! pred lis)
(check-arg procedure? pred break!)
(if (or (null-list? lis) (pred (car lis))) (values '() lis)
(let ((suffix (let lp ((prev lis) (rest (cdr lis)))
(if (null-list? rest) rest
(let ((x (car rest)))
(if (not (pred x)) (lp rest (cdr rest))
(begin (set-cdr! prev '())
rest)))))))
(values lis suffix))))
(define any
(case-lambda
;; Fast path 1
((pred lis1)
(check-arg procedure? pred any)
(and (not (null-list? lis1))
(let loop ((head (car lis1)) (tail (cdr lis1)))
(if (null-list? tail)
(pred head) ; Last PRED app is tail call.
(or (pred head) (loop (car tail) (cdr tail)))))))
;; Fast path 2
((pred lis1 lis2)
(check-arg procedure? pred any)
(and (not (null-list? lis1)) (not (null-list? lis2))
(let loop ((head1 (car lis1)) (tail1 (cdr lis1))
(head2 (car lis2)) (tail2 (cdr lis2)))
(if (or (null-list? tail1) (null-list? tail2))
(pred head1 head2) ; Last PRED app is tail call.
(or (pred head1 head2)
(loop (car tail1) (cdr tail1)
(car tail2) (cdr tail2)))))))
;; N-ary case
((pred lis1 lis2 . lists)
(check-arg procedure? pred any)
(and (not (null-list? lis1)) (not (null-list? lis2))
(receive (heads tails) (%cars+cdrs lists)
(and (pair? heads)
(let loop ((head1 (car lis1)) (tail1 (cdr lis1))
(head2 (car lis2)) (tail2 (cdr lis2))
(heads heads) (tails tails))
(if (or (null-list? tail1) (null-list? tail2))
(apply pred head1 head2 heads)
; Last PRED app is tail call.
(receive (next-heads next-tails) (%cars+cdrs tails)
(if (null? next-tails)
(apply pred head1 head2 heads)
; Last PRED app is tail call.
(or (apply pred head1 head2 heads)
(loop (car tail1) (cdr tail1)
(car tail2) (cdr tail2)
next-heads next-tails))))))))))))
;;(define (every pred list) ; Simple definition.
;; (let lp ((list list)) ; Doesn't return the last PRED value.
;; (or (not (pair? list))
;; (and (pred (car list))
;; (lp (cdr list))))))
(define every
(case-lambda
;; Fast path 1
((pred lis1)
(check-arg procedure? pred every)
(or (null-list? lis1)
(let loop ((head (car lis1)) (tail (cdr lis1)))
(if (null-list? tail)
(pred head) ; Last PRED app is tail call.
(and (pred head) (loop (car tail) (cdr tail)))))))
;; Fast path 2
((pred lis1 lis2)
(check-arg procedure? pred every)
(or (null-list? lis1) (null-list? lis2)
(let loop ((head1 (car lis1)) (tail1 (cdr lis1))
(head2 (car lis2)) (tail2 (cdr lis2)))
(if (or (null-list? tail1) (null-list? tail2))
(pred head1 head2) ; Last PRED app is tail call.
(and (pred head1 head2)
(loop (car tail1) (cdr tail1)
(car tail2) (cdr tail2)))))))
;; N-ary case
((pred lis1 lis2 . lists)
(check-arg procedure? pred every)
(or (null-list? lis1) (null-list? lis2)
(receive (heads tails) (%cars+cdrs lists)
(or (not (pair? heads))
(let loop ((head1 (car lis1)) (tail1 (cdr lis1))
(head2 (car lis2)) (tail2 (cdr lis2))
(heads heads) (tails tails))
(if (or (null-list? tail1) (null-list? tail2))
(apply pred head1 head2 heads)
; Last PRED app is tail call.
(receive (next-heads next-tails) (%cars+cdrs tails)
(if (null? next-tails)
(apply pred head1 head2 heads)
; Last PRED app is tail call.
(and (apply pred head1 head2 heads)
(loop (car tail1) (cdr tail1)
(car tail2) (cdr tail2)
next-heads next-tails))))))))))))
(define list-index
(case-lambda
;; Fast path 1
((pred lis1)
(check-arg procedure? pred list-index)
(let loop ((lis lis1)
(n 0))
(and (not (null-list? lis))
(if (pred (car lis)) n (loop (cdr lis) (fx+ n 1))))))
;; Fast path 2
((pred lis1 lis2)
(check-arg procedure? pred list-index)
(let loop ((lis1 lis1)
(lis2 lis2)
(n 0))
(and (not (or (null-list? lis1) (null-list? lis2)))
(if (pred (car lis1) (car lis2))
n
(loop (cdr lis1) (cdr lis2) (fx+ n 1))))))
;; N-ary case
((pred lis1 lis2 lis3 . lists)
(check-arg procedure? pred list-index)
(let loop ((lis1 lis1) (lis2 lis2) (lis3 lis3)
(lists lists)
(n 0))
(and (not (or (null-list? lis1) (null-list? lis2) (null-list? lis3)))
(receive (heads tails) (%cars+cdrs lists)
(and (not (null? heads))
(if (apply pred (car lis1) (car lis2) (car lis3) heads)
n
(loop (cdr lis1) (cdr lis2) (cdr lis3) tails
(fx+ n 1))))))))))
;;; Reverse
;;;;;;;;;;;
;R4RS, so not defined here.
;(define (reverse lis) (fold cons '() lis))
;(define (reverse! lis)
; (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair) '() lis))
(define (reverse! lis)
(let lp ((lis lis) (ans '()))
(if (null-list? lis) ans
(let ((tail (cdr lis)))
(set-cdr! lis ans)
(lp tail lis)))))
;;; Lists-as-sets
;;;;;;;;;;;;;;;;;
;;; This is carefully tuned code; do not modify casually.
;;; - It is careful to share storage when possible;
;;; - Side-effecting code tries not to perform redundant writes.
;;; - It tries to avoid linear-time scans in special cases where constant-time
;;; computations can be performed.
;;; - It relies on similar properties from the other list-lib procs it calls.
;;; For example, it uses the fact that the implementations of MEMBER and
;;; FILTER in this source code share longest common tails between args
;;; and results to get structure sharing in the lset procedures.
(define (%lset2<= = lis1 lis2) (every (lambda (x) (member x lis2 =)) lis1))
(define lset<=
(case-lambda
((=) (check-arg procedure? = lset<=) #t)
((= lis1) (check-arg procedure? = lset<=) #t)
((= lis1 lis2)
(check-arg procedure? = lset<=)
(or (eq? lis1 lis2) (%lset2<= = lis1 lis2)))
((= lis1 lis2 lis3 . lists)
(check-arg procedure? = lset<=)
(and (or (eq? lis1 lis2) (%lset2<= = lis1 lis2))
(or (eq? lis2 lis3) (%lset2<= = lis2 lis3))
(or (null? lists)
(let loop ((lis1 lis3)
(lis2 (car lists))
(lists (cdr lists)))
(and (or (eq? lis1 lis2) (%lset2<= = lis1 lis2))
(or (null? lists)
(loop lis2 (car lists) (cdr lists))))))))))
(define lset=
(case-lambda
((=) (check-arg procedure? = lset=) #t)
((= lis1) (check-arg procedure? = lset=) #t)
((= lis1 lis2)
(check-arg procedure? = lset=)
(or (eq? lis1 lis2)
(and (%lset2<= = lis1 lis2) (%lset2<= = lis2 lis1))))
((= lis1 lis2 lis3 . lists)
(check-arg procedure? = lset=)
(and (or (eq? lis1 lis2)
(and (%lset2<= = lis1 lis2) (%lset2<= = lis2 lis1)))
(or (eq? lis2 lis3)
(and (%lset2<= = lis2 lis3) (%lset2<= = lis3 lis2)))
(or (null? lists)
(let loop ((lis1 lis3)
(lis2 (car lists))
(lists (cdr lists)))
(and (or (eq? lis1 lis2)
(and (%lset2<= = lis1 lis2) (%lset2<= = lis2 lis1)))
(or (null? lists)
(loop lis2 (car lists) (cdr lists))))))))))
(define lset-adjoin
(case-lambda
((= lis) lis)
((= lis elt)
(check-arg procedure? = lset-adjoin)
(if (member elt lis) lis (cons elt lis)))
((= lis elt1 elt2)
(check-arg procedure? = lset-adjoin)
(let* ((lis (if (member elt1 lis) lis (cons elt1 lis)))
(lis (if (member elt2 lis) lis (cons elt2 lis))))
lis))
((= lis elt1 elt2 elt3 . elts)
(check-arg procedure? = lset-adjoin)
(let* ((lis (if (member elt1 lis) lis (cons elt1 lis)))
(lis (if (member elt2 lis) lis (cons elt2 lis)))
(lis (if (member elt3 lis) lis (cons elt3 lis))))
(if (null? elts) lis
(fold (lambda (elt ans) (if (member elt ans =) ans (cons elt ans)))
lis elts))))))
(define lset-union
(let ((lset-union-2
(lambda (= lis1 lis2)
(cond ((null? lis1) lis2) ; Don't copy any lists
((null? lis2) lis1) ; if we don't have to.
((eq? lis1 lis2) lis1)
(else
(fold (lambda (elt ans)
(if (member elt ans =)
ans
(cons elt ans)))
lis1 lis2))))))
(case-lambda
((=) (check-arg procedure? = lset-union) '())
((= lis1) (check-arg procedure? = lset-union) lis1)
((= lis1 lis2)
(check-arg procedure? = lset-union)
(lset-union-2 = lis1 lis2))
((= lis1 lis2 lis3 . lists)
(check-arg procedure? = lset-union)
(let* ((lis (lset-union-2 = lis1 lis2))
(lis (lset-union-2 = lis lis3)))
(if (null? lists) lis
(fold (lambda (lis2 lis1) (lset-union-2 = lis1 lis2))
lis lists)))))))
(define lset-union!
(let ((lset-union-2!
(lambda (= lis1 lis2)
(cond ((null? lis1) lis2) ; Don't copy any lists
((null? lis2) lis1) ; if we don't have to.
((eq? lis1 lis2) lis1)
(else
(pair-fold (lambda (pair ans)
(let ((elt (car pair)))
(if (member elt ans =)
ans
(begin (set-cdr! pair ans) pair))))
lis1 lis2))))))
(case-lambda
((=)
(check-arg procedure? = lset-union!)
'())
((= lis1) (check-arg procedure? = lset-union!) lis1)
((= lis1 lis2) ; Splice new elts of LIS1 onto the front of LIS2.
(check-arg procedure? = lset-union!)
(lset-union-2! = lis1 lis2))
((= lis1 lis2 lis3 . lists)
(check-arg procedure? = lset-union!)
(let* ((lis (lset-union-2! = lis1 lis2))
(lis (lset-union-2! = lis lis3)))
(if (null? lists) lis
(fold (lambda (lis1 lis2) (lset-union-2! = lis1 lis2))
lis lists)))))))
(define lset-intersection
(case-lambda
((= lis1)
(check-arg procedure? = lset-intersection)
lis1)
((= lis1 lis2)
(check-arg procedure? = lset-intersection)
(cond
((or (null-list? lis1) (eq? lis1 lis2))
lis1)
((null-list? lis2) lis2)
(else (filter (lambda (x) (member x lis2 =)) lis1))))
((= lis1 lis2 lis3 . lists)
(check-arg procedure? = lset-intersection)
(cond
;; Short cut
((or (null-list? lis1) (null-list? lis2) (null-list? lis3)
(any null-list? lists))
'())
;; Throw out lis2 (and lis3) if it is lis1
((eq? lis2 lis1)
(if (eq? lis3 lis1)
(apply lset-intersection = lis1 lists)
(apply lset-intersection = lis1 lis3 lists)))
;; Throw out lis3 if it is either lis1 or lis2
((or (eq? lis3 lis1) (eq? lis3 lis2))
(apply lset-intersection = lis1 lis2 lists))
;; Real procedure
(else
(let* ((lists (remove (lambda (lis)
(or (eq? lis lis1) (eq? lis lis2) (eq? lis lis3)))
lists)))
(filter (lambda (x)
(and (member x lis2 =)
(member x lis3 =)
(every (lambda (lis) (member x lis =))
lists)))
lis1)))))))
(define lset-intersection!
(case-lambda
((= lis1) (check-arg procedure? = lset-intersection!) lis1)
((= lis1 lis2)
(check-arg procedure? = lset-intersection!)
(cond
((or (null-list? lis2) (eq? lis1 lis2))
lis1)
((null-list? lis1) lis2)
(else (filter! (lambda (x) (member x lis2 =)) lis1))))
((= lis1 lis2 lis3 . lists)
(check-arg procedure? = lset-intersection!)
(cond
;; Short cut
((or (null-list? lis1) (null-list? lis2) (null-list? lis3)
(any null-list? lists))
'())
;; Throw out lis2 (and lis3) if it is lis1
((eq? lis2 lis1)
(if (eq? lis3 lis1)
(apply lset-intersection! = lis1 lists)
(apply lset-intersection! = lis1 lis3 lists)))
;; Throw out lis3 if it is either lis1 or lis2
((or (eq? lis3 lis1) (eq? lis3 lis2))
(apply lset-intersection! = lis1 lis2 lists))
;; Real procedure
(else
(let ((lists (remove (lambda (lis)
(or (eq? lis lis1) (eq? lis lis2) (eq? lis lis3)))
lists))) ; Remove duplicates
(filter! (lambda (x)
(and (member x lis2 =)
(member x lis3 =)
(every (lambda (lis) (member x lis =))
lists)))
lis1)))))))
(define lset-difference
(case-lambda
((= lis1) (check-arg procedure? = lset-difference) lis1)
((= lis1 lis2)
(check-arg procedure? = lset-difference)
(cond
((null-list? lis2) lis1)
((or (null-list? lis1) (eq? lis1 lis2))
'())
(else (filter (lambda (x) (not (member x lis2 =))) lis1))))
((= lis1 lis2 lis3 . lists)
(check-arg procedure? = lset-difference)
(cond
;; Short cut
((or (null-list? lis1) (eq? lis1 lis2) (eq? lis1 lis3)
(memq lis1 lists))
'())
;; Throw out lis2 (or lis3) if it is nil
((null? lis2)
(if (null? lis3)
(apply lset-difference lis1 lists)
(apply lset-difference lis1 lis3 lists)))
;; Throw out lis3 if it is lis2 or nil
((or (null? lis3) (eq? lis3 lis2))
(apply lset-difference lis1 lis2 lists))
;; Real procedure
(else
(let ((lists (remove (lambda (lis)
(or (null? lis) (eq? lis lis2) (eq? lis lis3)))
lists))) ; Remove nil, lis2 and lis3
(filter (lambda (x)
(and (not (member x lis2 =))
(not (member x lis3 =))
(every (lambda (lis) (not (member x lis =)))
lists)))
lis1)))))))
(define lset-difference!
(case-lambda
((= lis1) (check-arg procedure? = lset-difference!) lis1)
((= lis1 lis2)
(check-arg procedure? = lset-difference!)
(cond
((null-list? lis2) lis1)
((or (null-list? lis1) (eq? lis1 lis2))
'())
(else (filter! (lambda (x) (not (member x lis2 =))) lis1))))
((= lis1 lis2 lis3 . lists)
(check-arg procedure? = lset-difference!)
(cond
;; Short cut
((or (null-list? lis1) (eq? lis1 lis2) (eq? lis1 lis3)
(memq lis1 lists))
'())
;; Throw out lis2 (or lis3) if it is nil
((null? lis2)
(if (null? lis3)
(apply lset-difference lis1 lists)
(apply lset-difference lis1 lis3 lists)))
;; Throw out lis3 if it is lis2 or nil
((or (null? lis3) (eq? lis3 lis2))
(apply lset-difference lis1 lis2 lists))
;; Real procedure
(else
(let ((lists (remove (lambda (lis)
(or (null? lis) (eq? lis lis2) (eq? lis lis3)))
lists))) ; Remove nil, lis2 and lis3
(filter! (lambda (x)
(and (not (member x lis2 =))
(every (lambda (lis) (not (member x lis =)))
lists)))
lis1)))))))
(define lset-xor
(let ((lset-xor-2
(lambda (= b a) ; Compute A xor B:
;; Note that this code relies on the constant-time
;; short-cuts provided by LSET-DIFF+INTERSECTION,
;; LSET-DIFFERENCE & APPEND to provide constant-time short
;; cuts for the cases A = (), B = (), and A eq? B. It takes
;; a careful case analysis to see it, but it's carefully
;; built in.
;; Compute a-b and a^b, then compute b-(a^b) and
;; cons it onto the front of a-b.
(receive (a-b a-int-b) (lset-diff+intersection = a b)
(cond ((null? a-b) (lset-difference = b a))
((null? a-int-b) (append b a))
(else (fold (lambda (xb ans)
(if (member xb a-int-b =) ans (cons xb ans)))
a-b
b)))))))
(case-lambda
((=) (check-arg procedure? = lset-xor) '())
((= a) (check-arg procedure? = lset-xor) a)
((= a b)
(check-arg procedure? = lset-xor)
(lset-xor-2 = b a))
((= a b c . lists)
(check-arg procedure? = lset-xor)
(let* ((lis (lset-xor-2 = b a))
(lis (lset-xor-2 = c lis)))
(if (null? lists) lis
(fold (lambda (b a) (lset-xor-2 = b a))
lis lists)))))))
(define lset-xor!
(let ((lset-xor-2!
(lambda (= b a) ; Compute A xor B:
;; Note that this code relies on the constant-time
;; short-cuts provided by LSET-DIFF+INTERSECTION,
;; LSET-DIFFERENCE & APPEND to provide constant-time short
;; cuts for the cases A = (), B = (), and A eq? B. It takes
;; a careful case analysis to see it, but it's carefully
;; built in.
;; Compute a-b and a^b, then compute b-(a^b) and
;; cons it onto the front of a-b.
(receive (a-b a-int-b) (lset-diff+intersection! = a b)
(cond ((null? a-b) (lset-difference! = b a))
((null? a-int-b) (append! b a))
(else (pair-fold (lambda (b-pair ans)
(if (member (car b-pair) a-int-b =) ans
(begin (set-cdr! b-pair ans) b-pair)))
a-b
b)))))))
(case-lambda
((=) (check-arg procedure? = lset-xor!) '())
((= a) (check-arg procedure? = lset-xor!) a)
((= a b)
(check-arg procedure? = lset-xor!)
(lset-xor-2! = b a))
((= a b c . lists)
(check-arg procedure? = lset-xor!)
(let* ((lis (lset-xor-2! = b a))
(lis (lset-xor-2! = c lis)))
(if (null? lists) lis
(fold (lambda (b a) (lset-xor-2! = b a))
lis lists)))))))
(define lset-diff+intersection
(case-lambda
;; Fast path 1
((= lis1)
(check-arg procedure? = lset-diff+intersection)
(values lis1 '()))
;; Fast path 2
((= lis1 lis2)
(check-arg procedure? = lset-diff+intersection)
(cond
((or (null-list? lis1) (eq? lis1 lis2)) (values '() lis1))
((null-list? lis2) (values lis1 '()))
(else (partition (lambda (elt)
(not (member elt lis2 =)))
lis1))))
;; N-ary case
((= lis1 lis2 lis3 . lists)
(check-arg procedure? = lset-diff+intersection)
(cond
((or (null-list? lis1) (eq? lis1 lis2) (eq? lis1 lis3)
(memq lis1 lists))
(values '() lis1))
((null-list? lis2)
(if (null-list? lis3)
(apply lset-diff+intersection = lis1 lists)
(apply lset-diff+intersection = lis1 lis3 lists)))
((or (null-list? lis3) (eq? lis3 lis2))
(apply lset-diff+intersection = lis1 lis2 lists))
(else
(let ((lists (remove (lambda (lis)
(or (null? lis) (eq? lis lis2) (eq? lis lis3)))
lists)))
(partition (lambda (elt)
(not (or (member elt lis2 =)
(member elt lis3 =)
(any (lambda (lis) (member elt lis =))
lists))))
lis1)))))))
(define lset-diff+intersection!
(case-lambda
;; Fast path 1
((= lis1)
(check-arg procedure? = lset-diff+intersection!)
(values lis1 '()))
;; Fast path 2
((= lis1 lis2)
(check-arg procedure? = lset-diff+intersection!)
(cond
((or (null-list? lis1) (eq? lis1 lis2)) (values '() lis1))
((null-list? lis2) (values lis1 '()))
(else (partition! (lambda (elt)
(not (member elt lis2 =)))
lis1))))
;; N-ary case
((= lis1 lis2 lis3 . lists)
(check-arg procedure? = lset-diff+intersection!)
(cond
((or (null-list? lis1) (eq? lis1 lis2) (eq? lis1 lis3)
(memq lis1 lists))
(values '() lis1))
((null-list? lis2)
(if (null-list? lis3)
(apply lset-diff+intersection! = lis1 lists)
(apply lset-diff+intersection! = lis1 lis3 lists)))
((or (null-list? lis3) (eq? lis3 lis2))
(apply lset-diff+intersection = lis1 lis2 lists))
(else
(let ((lists (remove (lambda (lis)
(or (null? lis) (eq? lis lis2) (eq? lis lis3)))
lists)))
(partition! (lambda (elt)
(not (or (member elt lis2 =)
(member elt lis3 =)
(any (lambda (lis) (member elt lis =))
lists))))
lis1)))))))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a11.sls 0000664 0000000 0000000 00000000251 14737542645 0017734 0 ustar 00root root 0000000 0000000 #!r6rs
;; Automatically generated by private/make-aliased-libraries.sps
(library (srfi :11)
(export
let*-values
let-values)
(import (srfi :11 let-values))
)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a11/ 0000775 0000000 0000000 00000000000 14737542645 0017213 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a11/let-values.sls 0000664 0000000 0000000 00000000441 14737542645 0022016 0 ustar 00root root 0000000 0000000 #!r6rs
;; Copyright 2009 Derick Eddington. My MIT-style license is in the file named
;; LICENSE from the original collection this file is distributed with.
(library (srfi :11 let-values)
(export
let-values
let*-values)
(import
(only (rnrs) let-values let*-values))
)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a111.sls 0000664 0000000 0000000 00000000122 14737542645 0020012 0 ustar 00root root 0000000 0000000 (library (srfi :111) (export box box? unbox set-box!) (import (srfi :111 boxes)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a111/ 0000775 0000000 0000000 00000000000 14737542645 0017274 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a111/boxes.sls 0000664 0000000 0000000 00000000166 14737542645 0021142 0 ustar 00root root 0000000 0000000 (library (srfi :111 boxes)
(export box box? unbox set-box!)
(import (only (chezscheme) box box? unbox set-box!)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a113.sls 0000664 0000000 0000000 00000002702 14737542645 0020022 0 ustar 00root root 0000000 0000000 (library (srfi :113)
(export
set set-unfold
set? set-contains? set-empty? set-disjoint?
set-member set-element-comparator
set-adjoin set-adjoin! set-replace set-replace!
set-delete set-delete! set-delete-all set-delete-all! set-search!
set-size set-find set-count set-any? set-every?
set-map set-for-each set-fold
set-filter set-remove set-remove set-partition
set-filter! set-remove! set-partition!
set-copy set->list list->set list->set!
set=? set set>? set<=? set>=?
set-union set-intersection set-difference set-xor
set-union! set-intersection! set-difference! set-xor!
set-comparator
bag bag-unfold
bag? bag-contains? bag-empty? bag-disjoint?
bag-member bag-element-comparator
bag-adjoin bag-adjoin! bag-replace bag-replace!
bag-delete bag-delete! bag-delete-all bag-delete-all! bag-search!
bag-size bag-find bag-count bag-any? bag-every?
bag-map bag-for-each bag-fold
bag-filter bag-remove bag-partition
bag-filter! bag-remove! bag-partition!
bag-copy bag->list list->bag list->bag!
bag=? bag bag>? bag<=? bag>=?
bag-union bag-intersection bag-difference bag-xor
bag-union! bag-intersection! bag-difference! bag-xor!
bag-comparator
bag-sum bag-sum! bag-product bag-product!
bag-unique-size bag-element-count bag-for-each-unique bag-fold-unique
bag-increment! bag-decrement! bag->set set->bag set->bag!
bag->alist alist->bag)
(import (srfi :113 sets-and-bags)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a113/ 0000775 0000000 0000000 00000000000 14737542645 0017276 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a113/sets-and-bags.sls 0000664 0000000 0000000 00000122742 14737542645 0022461 0 ustar 00root root 0000000 0000000 (library (srfi :113 sets-and-bags)
(export
set set-unfold
set? set-contains? set-empty? set-disjoint?
set-member set-element-comparator
set-adjoin set-adjoin! set-replace set-replace!
set-delete set-delete! set-delete-all set-delete-all! set-search!
set-size set-find set-count set-any? set-every?
set-map set-for-each set-fold
set-filter set-remove set-partition
set-filter! set-remove! set-partition!
set-copy set->list list->set list->set!
set=? set set>? set<=? set>=?
set-union set-intersection set-difference set-xor
set-union! set-intersection! set-difference! set-xor!
set-comparator
bag bag-unfold
bag? bag-contains? bag-empty? bag-disjoint?
bag-member bag-element-comparator
bag-adjoin bag-adjoin! bag-replace bag-replace!
bag-delete bag-delete! bag-delete-all bag-delete-all! bag-search!
bag-size bag-find bag-count bag-any? bag-every?
bag-map bag-for-each bag-fold
bag-filter bag-remove bag-partition
bag-filter! bag-remove! bag-partition!
bag-copy bag->list list->bag list->bag!
bag=? bag bag>? bag<=? bag>=?
bag-union bag-intersection bag-difference bag-xor
bag-union! bag-intersection! bag-difference! bag-xor!
bag-comparator
bag-sum bag-sum! bag-product bag-product!
bag-unique-size bag-element-count bag-for-each-unique bag-fold-unique
bag-increment! bag-decrement! bag->set set->bag set->bag!
bag->alist alist->bag)
(import (except (rnrs) define-record-type)
(only (rnrs r5rs) modulo)
(srfi :9)
(srfi :128 comparators)
(srfi :125 hashtables))
(begin
;;;; Implementation of general sets and bags for SRFI 113
;;; A "sob" object is the representation of both sets and bags.
;;; This allows each set-* and bag-* procedure to be implemented
;;; using the same code, without having to deal in ugly indirections
;;; over the field accessors. There are three fields, "sob-multi?",
;;; "sob-hash-table", and "sob-comparator."
;;; The value of "sob-multi?" is #t for bags and #f for sets.
;;; "Sob-hash-table" maps the elements of the sob to the number of times
;;; the element appears, which is always 1 for a set, any positive value
;;; for a bag. "Sob-comparator" is the comparator for the elements of
;;; the set.
;;; Note that sob-* procedures do not do type checking or (typically) the
;;; copying required for supporting pure functional update. These things
;;; are done by the set-* and bag-* procedures, which are externally
;;; exposed (but trivial and mostly uncommented below).
;;; Shim to convert from SRFI 69 to the future "intermediate hash tables"
;;; SRFI. Unfortunately, hash-table-fold is incompatible between the two
;;; and so is not usable.
;; This will be just "make-hash-table" in future.
(define (make-hash-table/comparator comparator)
(make-hash-table comparator))
;; These two procedures adjust for the mismatch between the hash functions
;; of SRFI 114, which return a potentially unbounded non-negative integer,
;; and the hash functions of SRFI 69, which expect to be able to pass
;; a second argument which is an upper bound.
(define (modulizer hash-function)
(case-lambda
((obj) (hash-function obj))
((obj limit) (modulo (hash-function obj) limit))))
;;; Record definition and core typing/checking procedures
(define-record-type sob
(raw-make-sob hash-table comparator multi?)
sob?
(hash-table sob-hash-table)
(comparator sob-comparator)
(multi? sob-multi?))
(define (set? obj) (and (sob? obj) (not (sob-multi? obj))))
(define (bag? obj) (and (sob? obj) (sob-multi? obj)))
(define (check-set obj) (if (not (set? obj)) (error "not a set" obj)))
(define (check-bag obj) (if (not (bag? obj)) (error "not a bag" obj)))
;; These procedures verify that not only are their arguments all sets
;; or all bags as the case may be, but also share the same comparator.
(define (check-all-sets list)
(for-each (lambda (obj) (check-set obj)) list)
(sob-check-comparators list))
(define (check-all-bags list)
(for-each (lambda (obj) (check-bag obj)) list)
(sob-check-comparators list))
(define (sob-check-comparators list)
(if (not (null? list))
(for-each
(lambda (sob)
(check-same-comparator (car list) sob))
(cdr list))))
;; This procedure is used directly when there are exactly two arguments.
(define (check-same-comparator a b)
(if (not (eq? (sob-comparator a) (sob-comparator b)))
(error "different comparators" a b)))
;; This procedure defends against inserting an element
;; into a sob that violates its constructor, since
;; typical hash-table implementations don't check for us.
(define (check-element sob element)
(comparator-check-type (sob-comparator sob) element))
;;; Constructors
;; Construct an arbitrary empty sob out of nothing.
(define (make-sob comparator multi?)
(raw-make-sob (make-hash-table/comparator comparator) comparator multi?))
;; Copy a sob, sharing the constructor.
(define (sob-copy sob)
(raw-make-sob (hash-table-copy (sob-hash-table sob) #t)
(sob-comparator sob)
(sob-multi? sob)))
(define (set-copy set)
(check-set set)
(sob-copy set))
(define (bag-copy bag)
(check-bag bag)
(sob-copy bag))
;; Construct an empty sob that shares the constructor of an existing sob.
(define (sob-empty-copy sob)
(make-sob (sob-comparator sob) (sob-multi? sob)))
;; Construct a set or a bag and insert elements into it. These are the
;; simplest external constructors.
(define (set comparator . elements)
(let ((result (make-sob comparator #f)))
(for-each (lambda (x) (sob-increment! result x 1)) elements)
result))
(define (bag comparator . elements)
(let ((result (make-sob comparator #t)))
(for-each (lambda (x) (sob-increment! result x 1)) elements)
result))
;; The fundamental (as opposed to simplest) constructor: unfold the
;; results of iterating a function as a set. In line with SRFI 1,
;; we provide an opportunity to map the sequence of seeds through a
;; mapper function.
(define (sob-unfold stop? mapper successor seed comparator multi?)
(let ((result (make-sob comparator multi?)))
(let loop ((seed seed))
(if (stop? seed)
result
(begin
(sob-increment! result (mapper seed) 1)
(loop (successor seed)))))))
(define (set-unfold continue? mapper successor seed comparator)
(sob-unfold continue? mapper successor seed comparator #f))
(define (bag-unfold continue? mapper successor seed comparator)
(sob-unfold continue? mapper successor seed comparator #t))
;;; Predicates
;; Just a wrapper of hash-table-contains?.
(define (sob-contains? sob member)
(hash-table-contains? (sob-hash-table sob) member))
(define (set-contains? set member)
(check-set set)
(sob-contains? set member))
(define (bag-contains? bag member)
(check-bag bag)
(sob-contains? bag member))
;; A sob is empty if its size is 0.
(define (sob-empty? sob)
(= 0 (hash-table-size (sob-hash-table sob))))
(define (set-empty? set)
(check-set set)
(sob-empty? set))
(define (bag-empty? bag)
(check-bag bag)
(sob-empty? bag))
;; Two sobs are disjoint if, when looping through one, we can't find
;; any of its elements in the other. We have to try both ways:
;; sob-half-disjoint checks just one direction for simplicity.
(define (sob-half-disjoint? a b)
(let ((ha (sob-hash-table a))
(hb (sob-hash-table b)))
(call/cc
(lambda (return)
(hash-table-for-each
(lambda (key val) (if (hash-table-contains? hb key) (return #f)))
ha)
#t))))
(define (set-disjoint? a b)
(check-set a)
(check-set b)
(check-same-comparator a b)
(and (sob-half-disjoint? a b) (sob-half-disjoint? b a)))
(define (bag-disjoint? a b)
(check-bag a)
(check-bag b)
(check-same-comparator a b)
(and (sob-half-disjoint? a b) (sob-half-disjoint? b a)))
;; Accessors
;; If two objects are indistinguishable by the comparator's
;; equality procedure, only one of them will be represented in the sob.
;; This procedure lets us find out which one it is; it will return
;; the value stored in the sob that is equal to the element.
;; Note that we have to search the whole hash table item by item.
;; The default is returned if there is no such element.
(define (sob-member sob element default)
(define (same? a b) (=? (sob-comparator sob) a b))
(call/cc
(lambda (return)
(hash-table-for-each
(lambda (key val) (if (same? key element) (return key)))
(sob-hash-table sob))
default)))
(define (set-member set element default)
(check-set set)
(sob-member set element default))
(define (bag-member bag element default)
(check-bag bag)
(sob-member bag element default))
;; Retrieve the comparator.
(define (set-element-comparator set)
(check-set set)
(sob-comparator set))
(define (bag-element-comparator bag)
(check-bag bag)
(sob-comparator bag))
;; Updaters (pure functional and linear update)
;; The primitive operation for adding an element to a sob.
;; There are a few cases where we bypass this for efficiency.
(define (sob-increment! sob element count)
(check-element sob element)
(hash-table-update!/default
(sob-hash-table sob)
element
(if (sob-multi? sob)
(lambda (value) (+ value count))
(lambda (value) 1))
0))
;; The primitive operation for removing an element from a sob. Note this
;; procedure is incomplete: it allows the count of an element to drop below 1.
;; Therefore, whenever it is used it is necessary to call sob-cleanup!
;; to fix things up. This is done because it is unsafe to remove an
;; object from a hash table while iterating through it.
(define (sob-decrement! sob element count)
(hash-table-update!/default
(sob-hash-table sob)
element
(lambda (value) (- value count))
0))
;; This is the cleanup procedure, which happens in two passes: it
;; iterates through the sob, deciding which elements to remove (those
;; with non-positive counts), and collecting them in a list. When the
;; iteration is done, it is safe to remove the elements using the list,
;; because we are no longer iterating over the hash table. It returns
;; its argument, because it is often tail-called at the end of some
;; procedure that wants to return the clean sob.
(define (sob-cleanup! sob)
(let ((ht (sob-hash-table sob)))
(for-each (lambda (key) (hash-table-delete! ht key))
(nonpositive-keys ht))
sob))
(define (nonpositive-keys ht)
(let ((result '()))
(hash-table-for-each
(lambda (key value)
(when (<= value 0)
(set! result (cons key result))))
ht)
result))
;; We expose these for bags but not sets.
(define (bag-increment! bag element count)
(check-bag bag)
(sob-increment! bag element count)
bag)
(define (bag-decrement! bag element count)
(check-bag bag)
(sob-decrement! bag element count)
(sob-cleanup! bag)
bag)
;; The primitive operation to add elements from a list. We expose
;; this two ways: with a list argument and with multiple arguments.
(define (sob-adjoin-all! sob elements)
(for-each
(lambda (elem)
(sob-increment! sob elem 1))
elements))
(define (set-adjoin! set . elements)
(check-set set)
(sob-adjoin-all! set elements)
set)
(define (bag-adjoin! bag . elements)
(check-bag bag)
(sob-adjoin-all! bag elements)
bag)
;; These versions copy the set or bag before adjoining.
(define (set-adjoin set . elements)
(check-set set)
(let ((result (sob-copy set)))
(sob-adjoin-all! result elements)
result))
(define (bag-adjoin bag . elements)
(check-bag bag)
(let ((result (sob-copy bag)))
(sob-adjoin-all! result elements)
result))
;; Given an element which resides in a set, this makes sure that the
;; specified element is represented by the form given. Thus if a
;; sob contains 2 and the equality predicate is =, then calling
;; (sob-replace! sob 2.0) will replace the 2 with 2.0. Does nothing
;; if there is no such element in the sob.
(define (sob-replace! sob element)
(let* ((comparator (sob-comparator sob))
(= (comparator-equality-predicate comparator))
(ht (sob-hash-table sob)))
(comparator-check-type comparator element)
(call/cc
(lambda (return)
(hash-table-for-each
(lambda (key value)
(when (= key element)
(hash-table-delete! ht key)
(hash-table-set! ht element value)
(return sob)))
ht)
sob))))
(define (set-replace! set element)
(check-set set)
(sob-replace! set element)
set)
(define (bag-replace! bag element)
(check-bag bag)
(sob-replace! bag element)
bag)
;; Non-destructive versions that copy the set first. Yes, a little
;; bit inefficient because it copies the element to be replaced before
;; actually replacing it.
(define (set-replace set element)
(check-set set)
(let ((result (sob-copy set)))
(sob-replace! result element)
result))
(define (bag-replace bag element)
(check-bag bag)
(let ((result (sob-copy bag)))
(sob-replace! result element)
result))
;; The primitive operation to delete elemnets from a list.
;; Like sob-adjoin-all!, this is exposed two ways. It calls
;; sob-cleanup! itself, so its callers don't need to (though it is safe
;; to do so.)
(define (sob-delete-all! sob elements)
(for-each (lambda (element) (sob-decrement! sob element 1)) elements)
(sob-cleanup! sob)
sob)
(define (set-delete! set . elements)
(check-set set)
(sob-delete-all! set elements))
(define (bag-delete! bag . elements)
(check-bag bag)
(sob-delete-all! bag elements))
(define (set-delete-all! set elements)
(check-set set)
(sob-delete-all! set elements))
(define (bag-delete-all! bag elements)
(check-bag bag)
(sob-delete-all! bag elements))
;; Non-destructive version copy first; this is inefficient.
(define (set-delete set . elements)
(check-set set)
(sob-delete-all! (sob-copy set) elements))
(define (bag-delete bag . elements)
(check-bag bag)
(sob-delete-all! (sob-copy bag) elements))
(define (set-delete-all set elements)
(check-set set)
(sob-delete-all! (sob-copy set) elements))
(define (bag-delete-all bag elements)
(check-bag bag)
(sob-delete-all! (sob-copy bag) elements))
;; Flag used by sob-search! to represent a missing object.
(define missing (string-copy "missing"))
;; Searches and then dispatches to user-defined procedures on failure
;; and success, which in turn should reinvoke a procedure to take some
;; action on the set (insert, ignore, replace, or remove).
(define (sob-search! sob element failure success)
(define (insert obj)
(sob-increment! sob element 1)
(values sob obj))
(define (ignore obj)
(values sob obj))
(define (update new-elem obj)
(sob-decrement! sob element 1)
(sob-increment! sob new-elem 1)
(values (sob-cleanup! sob) obj))
(define (remove obj)
(sob-decrement! sob element 1)
(values (sob-cleanup! sob) obj))
(let ((true-element (sob-member sob element missing)))
(if (eq? true-element missing)
(failure insert ignore)
(success true-element update remove))))
(define (set-search! set element failure success)
(check-set set)
(sob-search! set element failure success))
(define (bag-search! bag element failure success)
(check-bag bag)
(sob-search! bag element failure success))
;; Return the size of a sob. If it's a set, we can just use the
;; number of associations in the hash table, but if it's a bag, we
;; have to add up the counts.
(define (sob-size sob)
(if (sob-multi? sob)
(let ((result 0))
(hash-table-for-each
(lambda (elem count) (set! result (+ count result)))
(sob-hash-table sob))
result)
(hash-table-size (sob-hash-table sob))))
(define (set-size set)
(check-set set)
(sob-size set))
(define (bag-size bag)
(check-bag bag)
(sob-size bag))
;; Search a sob to find something that matches a predicate. You don't
;; know which element you will get, so this is not as useful as finding
;; an element in a list or other ordered container. If it's not there,
;; call the failure thunk.
(define (sob-find pred sob failure)
(call/cc
(lambda (return)
(hash-table-for-each
(lambda (key value)
(if (pred key) (return key)))
(sob-hash-table sob))
(failure))))
(define (set-find pred set failure)
(check-set set)
(sob-find pred set failure))
(define (bag-find pred bag failure)
(check-bag bag)
(sob-find pred bag failure))
;; Count the number of elements in the sob that satisfy the predicate.
;; This is a special case of folding.
(define (sob-count pred sob)
(sob-fold
(lambda (elem total) (if (pred elem) (+ total 1) total))
0
sob))
(define (set-count pred set)
(check-set set)
(sob-count pred set))
(define (bag-count pred bag)
(check-bag bag)
(sob-count pred bag))
;; Check if any of the elements in a sob satisfy a predicate. Breaks out
;; early (with call/cc) if a success is found.
(define (sob-any? pred sob)
(call/cc
(lambda (return)
(hash-table-for-each
(lambda (elem value) (if (pred elem) (return #t)))
(sob-hash-table sob))
#f)))
(define (set-any? pred set)
(check-set set)
(sob-any? pred set))
(define (bag-any? pred bag)
(check-bag bag)
(sob-any? pred bag))
;; Analogous to set-any?. Breaks out early if a failure is found.
(define (sob-every? pred sob)
(call/cc
(lambda (return)
(hash-table-for-each
(lambda (elem value) (if (not (pred elem)) (return #f)))
(sob-hash-table sob))
#t)))
(define (set-every? pred set)
(check-set set)
(sob-every? pred set))
(define (bag-every? pred bag)
(check-bag bag)
(sob-every? pred bag))
;;; Mapping and folding
;; A utility for iterating a command n times. This is used by sob-for-each
;; to execute a procedure over the repeated elements in a bag. Because
;; of the representation of sets, it works for them too.
(define (do-n-times cmd n)
(let loop ((n n))
(when (> n 0)
(cmd)
(loop (- n 1)))))
;; Basic iterator over a sob.
(define (sob-for-each proc sob)
(hash-table-for-each
(lambda (key value) (do-n-times (lambda () (proc key)) value))
(sob-hash-table sob)))
(define (set-for-each proc set)
(check-set set)
(sob-for-each proc set))
(define (bag-for-each proc bag)
(check-bag bag)
(sob-for-each proc bag))
;; Fundamental mapping operator. We map over the associations directly,
;; because each instance of an element in a bag will be treated identically
;; anyway; we insert them all at once with sob-increment!.
(define (sob-map comparator proc sob)
(let ((result (make-sob comparator (sob-multi? sob))))
(hash-table-for-each
(lambda (key value) (sob-increment! result (proc key) value))
(sob-hash-table sob))
result))
(define (set-map comparator proc set)
(check-set set)
(sob-map comparator proc set))
(define (bag-map comparator proc bag)
(check-bag bag)
(sob-map comparator proc bag))
;; The fundamental deconstructor. Note that there are no left vs. right
;; folds because there is no order. Each element in a bag is fed into
;; the fold separately.
(define (sob-fold proc nil sob)
(let ((result nil))
(sob-for-each
(lambda (elem) (set! result (proc elem result)))
sob)
result))
(define (set-fold proc nil set)
(check-set set)
(sob-fold proc nil set))
(define (bag-fold proc nil bag)
(check-bag bag)
(sob-fold proc nil bag))
;; Process every element and copy the ones that satisfy the predicate.
;; Identical elements are processed all at once. This is used for both
;; filter and remove.
(define (sob-filter pred sob)
(let ((result (sob-empty-copy sob)))
(hash-table-for-each
(lambda (key value)
(if (pred key) (sob-increment! result key value)))
(sob-hash-table sob))
result))
(define (set-filter pred set)
(check-set set)
(sob-filter pred set))
(define (bag-filter pred bag)
(check-bag bag)
(sob-filter pred bag))
(define (set-remove pred set)
(check-set set)
(sob-filter (lambda (x) (not (pred x))) set))
(define (bag-remove pred bag)
(check-bag bag)
(sob-filter (lambda (x) (not (pred x))) bag))
;; Process each element and remove those that don't satisfy the filter.
;; This does its own cleanup, and is used for both filter! and remove!.
(define (sob-filter! pred sob)
(hash-table-for-each
(lambda (key value)
(if (not (pred key)) (sob-decrement! sob key value)))
(sob-hash-table sob))
(sob-cleanup! sob))
(define (set-filter! pred set)
(check-set set)
(sob-filter! pred set))
(define (bag-filter! pred bag)
(check-bag bag)
(sob-filter! pred bag))
(define (set-remove! pred set)
(check-set set)
(sob-filter! (lambda (x) (not (pred x))) set))
(define (bag-remove! pred bag)
(check-bag bag)
(sob-filter! (lambda (x) (not (pred x))) bag))
;; Create two sobs and copy the elements that satisfy the predicate into
;; one of them, all others into the other. This is more efficient than
;; filtering and removing separately.
(define (sob-partition pred sob)
(let ((res1 (sob-empty-copy sob))
(res2 (sob-empty-copy sob)))
(hash-table-for-each
(lambda (key value)
(if (pred key)
(sob-increment! res1 key value)
(sob-increment! res2 key value)))
(sob-hash-table sob))
(values res1 res2)))
(define (set-partition pred set)
(check-set set)
(sob-partition pred set))
(define (bag-partition pred bag)
(check-bag bag)
(sob-partition pred bag))
;; Create a sob and iterate through the given sob. Anything that satisfies
;; the predicate is left alone; anything that doesn't is removed from the
;; given sob and added to the new sob.
(define (sob-partition! pred sob)
(let ((result (sob-empty-copy sob)))
(hash-table-for-each
(lambda (key value)
(if (not (pred key))
(begin
(sob-decrement! sob key value)
(sob-increment! result key value))))
(sob-hash-table sob))
(values (sob-cleanup! sob) result)))
(define (set-partition! pred set)
(check-set set)
(sob-partition! pred set))
(define (bag-partition! pred bag)
(check-bag bag)
(sob-partition! pred bag))
;;; Copying and conversion
;;; Convert a sob to a list; a special case of sob-fold.
(define (sob->list sob)
(sob-fold (lambda (elem list) (cons elem list)) '() sob))
(define (set->list set)
(check-set set)
(sob->list set))
(define (bag->list bag)
(check-bag bag)
(sob->list bag))
;; Convert a list to a sob. Probably could be done using unfold, but
;; since sobs are mutable anyway, it's just as easy to add the elements
;; by side effect.
(define (list->sob! sob list)
(for-each (lambda (elem) (sob-increment! sob elem 1)) list)
sob)
(define (list->set comparator list)
(list->sob! (make-sob comparator #f) list))
(define (list->bag comparator list)
(list->sob! (make-sob comparator #t) list))
(define (list->set! set list)
(check-set set)
(list->sob! set list))
(define (list->bag! bag list)
(check-bag bag)
(list->sob! bag list))
;;; Subsets
;; All of these procedures follow the same pattern. The
;; sob? procedures are case-lambdas that reduce the multi-argument
;; case to the two-argument case. As usual, the set? and
;; bag? procedures are trivial layers over the sob? procedure.
;; The dyadic-sob? procedures are where it gets interesting, so see
;; the comments on them.
(define sob=?
(case-lambda
((sob) #t)
((sob1 sob2) (dyadic-sob=? sob1 sob2))
((sob1 sob2 . sobs)
(and (dyadic-sob=? sob1 sob2)
(apply sob=? sob2 sobs)))))
(define (set=? . sets)
(check-all-sets sets)
(apply sob=? sets))
(define (bag=? . bags)
(check-all-bags bags)
(apply sob=? bags))
;; First we check that there are the same number of entries in the
;; hashtables of the two sobs; if that's not true, they can't be equal.
;; Then we check that for each key, the values are the same (where
;; being absent counts as a value of 0). If any values aren't equal,
;; again they can't be equal.
(define (dyadic-sob=? sob1 sob2)
(call/cc
(lambda (return)
(let ((ht1 (sob-hash-table sob1))
(ht2 (sob-hash-table sob2)))
(if (not (= (hash-table-size ht1) (hash-table-size ht2)))
(return #f))
(hash-table-for-each
(lambda (key value)
(if (not (= value (hash-table-ref/default ht2 key 0)))
(return #f)))
ht1))
#t)))
(define sob<=?
(case-lambda
((sob) #t)
((sob1 sob2) (dyadic-sob<=? sob1 sob2))
((sob1 sob2 . sobs)
(and (dyadic-sob<=? sob1 sob2)
(apply sob<=? sob2 sobs)))))
(define (set<=? . sets)
(check-all-sets sets)
(apply sob<=? sets))
(define (bag<=? . bags)
(check-all-bags bags)
(apply sob<=? bags))
;; This is analogous to dyadic-sob=?, except that we have to check
;; both sobs to make sure each value is <= in order to be sure
;; that we've traversed all the elements in either sob.
(define (dyadic-sob<=? sob1 sob2)
(call/cc
(lambda (return)
(let ((ht1 (sob-hash-table sob1))
(ht2 (sob-hash-table sob2)))
(if (not (<= (hash-table-size ht1) (hash-table-size ht2)))
(return #f))
(hash-table-for-each
(lambda (key value)
(if (not (<= value (hash-table-ref/default ht2 key 0)))
(return #f)))
ht1))
#t)))
(define sob
(case-lambda
((sob) #t)
((sob1 sob2) (dyadic-sob sob1 sob2))
((sob1 sob2 . sobs)
(and (dyadic-sob sob1 sob2)
(apply sob sob2 sobs)))))
(define (set . sets)
(check-all-sets sets)
(apply sob sets))
(define (bag . bags)
(check-all-bags bags)
(apply sob bags))
;; Strict subset test is a bit more involved. At least one entry in ht1
;; needs to have smaller value than the entry in ht2.
(define (dyadic-sob sob1 sob2)
(call/cc
(lambda (return)
(let ((ht1 (sob-hash-table sob1))
(ht2 (sob-hash-table sob2)))
(let ((smaller-count
(cond
((< (hash-table-size ht1) (hash-table-size ht2)) 1)
((= (hash-table-size ht1) (hash-table-size ht2)) 0)
(else (return #f)))))
(hash-table-for-each
(lambda (key value)
(let ((value2 (hash-table-ref/default ht2 key 0)))
(if (not (<= value value2))
(return #f)
(if (< value value2)
(set! smaller-count (+ smaller-count 1))))))
ht1)
(positive? smaller-count))))))
(define sob>?
(case-lambda
((sob) #t)
((sob1 sob2) (dyadic-sob>? sob1 sob2))
((sob1 sob2 . sobs)
(and (dyadic-sob>? sob1 sob2)
(apply sob>? sob2 sobs)))))
(define (set>? . sets)
(check-all-sets sets)
(apply sob>? sets))
(define (bag>? . bags)
(check-all-bags bags)
(apply sob>? bags))
;; > is the inverse of <. Again, this is only true dyadically.
(define (dyadic-sob>? sob1 sob2)
(dyadic-sob sob2 sob1))
(define sob>=?
(case-lambda
((sob) #t)
((sob1 sob2) (dyadic-sob>=? sob1 sob2))
((sob1 sob2 . sobs)
(and (dyadic-sob>=? sob1 sob2)
(apply sob>=? sob2 sobs)))))
(define (set>=? . sets)
(check-all-sets sets)
(apply sob>=? sets))
(define (bag>=? . bags)
(check-all-bags bags)
(apply sob>=? bags))
;; <= is the inverse of >=. Again, this is only true dyadically.
(define (dyadic-sob>=? sob1 sob2)
(dyadic-sob<=? sob2 sob1))
;;; Set theory operations
;; A trivial helper function which upper-bounds n by one if multi? is false.
(define (max-one n multi?)
(if multi? n (if (> n 1) 1 n)))
;; The logic of union, intersection, difference, and sum is the same: the
;; sob-* and sob-*! procedures do the reduction to the dyadic-sob-*!
;; procedures. The difference is that the sob-* procedures allocate
;; an empty copy of the first sob to accumulate the results in, whereas
;; the sob-*! procedures work directly in the first sob.
;; Note that there is no set-sum, as it is the same as set-union.
(define (sob-union sob1 . sobs)
(if (null? sobs)
sob1
(let ((result (sob-empty-copy sob1)))
(dyadic-sob-union! result sob1 (car sobs))
(for-each
(lambda (sob) (dyadic-sob-union! result result sob))
(cdr sobs))
result)))
;; For union, we take the max of the counts of each element found
;; in either sob and put that in the result. On the pass through
;; sob2, we know that the intersection is already accounted for,
;; so we just copy over things that aren't in sob1.
(define (dyadic-sob-union! result sob1 sob2)
(let ((sob1-ht (sob-hash-table sob1))
(sob2-ht (sob-hash-table sob2))
(result-ht (sob-hash-table result)))
(hash-table-for-each
(lambda (key value1)
(let ((value2 (hash-table-ref/default sob2-ht key 0)))
(hash-table-set! result-ht key (max value1 value2))))
sob1-ht)
(hash-table-for-each
(lambda (key value2)
(let ((value1 (hash-table-ref/default sob1-ht key 0)))
(if (= value1 0)
(hash-table-set! result-ht key value2))))
sob2-ht)))
(define (set-union . sets)
(check-all-sets sets)
(apply sob-union sets))
(define (bag-union . bags)
(check-all-bags bags)
(apply sob-union bags))
(define (sob-union! sob1 . sobs)
(for-each
(lambda (sob) (dyadic-sob-union! sob1 sob1 sob))
sobs)
sob1)
(define (set-union! . sets)
(check-all-sets sets)
(apply sob-union! sets))
(define (bag-union! . bags)
(check-all-bags bags)
(apply sob-union! bags))
(define (sob-intersection sob1 . sobs)
(if (null? sobs)
sob1
(let ((result (sob-empty-copy sob1)))
(dyadic-sob-intersection! result sob1 (car sobs))
(for-each
(lambda (sob) (dyadic-sob-intersection! result result sob))
(cdr sobs))
(sob-cleanup! result))))
;; For intersection, we compute the min of the counts of each element.
;; We only have to scan sob1. We clean up the result when we are
;; done, in case it is the same as sob1.
(define (dyadic-sob-intersection! result sob1 sob2)
(let ((sob1-ht (sob-hash-table sob1))
(sob2-ht (sob-hash-table sob2))
(result-ht (sob-hash-table result)))
(hash-table-for-each
(lambda (key value1)
(let ((value2 (hash-table-ref/default sob2-ht key 0)))
(hash-table-set! result-ht key (min value1 value2))))
sob1-ht)))
(define (set-intersection . sets)
(check-all-sets sets)
(apply sob-intersection sets))
(define (bag-intersection . bags)
(check-all-bags bags)
(apply sob-intersection bags))
(define (sob-intersection! sob1 . sobs)
(for-each
(lambda (sob) (dyadic-sob-intersection! sob1 sob1 sob))
sobs)
(sob-cleanup! sob1))
(define (set-intersection! . sets)
(check-all-sets sets)
(apply sob-intersection! sets))
(define (bag-intersection! . bags)
(check-all-bags bags)
(apply sob-intersection! bags))
(define (sob-difference sob1 . sobs)
(if (null? sobs)
sob1
(let ((result (sob-empty-copy sob1)))
(dyadic-sob-difference! result sob1 (car sobs))
(for-each
(lambda (sob) (dyadic-sob-difference! result result sob))
(cdr sobs))
(sob-cleanup! result))))
;; For difference, we use (big surprise) the numeric difference, bounded
;; by zero. We only need to scan sob1, but we clean up the result in
;; case it is the same as sob1.
(define (dyadic-sob-difference! result sob1 sob2)
(let ((sob1-ht (sob-hash-table sob1))
(sob2-ht (sob-hash-table sob2))
(result-ht (sob-hash-table result)))
(hash-table-for-each
(lambda (key value1)
(let ((value2 (hash-table-ref/default sob2-ht key 0)))
(hash-table-set! result-ht key (- value1 value2))))
sob1-ht)))
(define (set-difference . sets)
(check-all-sets sets)
(apply sob-difference sets))
(define (bag-difference . bags)
(check-all-bags bags)
(apply sob-difference bags))
(define (sob-difference! sob1 . sobs)
(for-each
(lambda (sob) (dyadic-sob-difference! sob1 sob1 sob))
sobs)
(sob-cleanup! sob1))
(define (set-difference! . sets)
(check-all-sets sets)
(apply sob-difference! sets))
(define (bag-difference! . bags)
(check-all-bags bags)
(apply sob-difference! bags))
(define (sob-sum sob1 . sobs)
(if (null? sobs)
sob1
(let ((result (sob-empty-copy sob1)))
(dyadic-sob-sum! result sob1 (car sobs))
(for-each
(lambda (sob) (dyadic-sob-sum! result result sob))
(cdr sobs))
result)))
;; Sum is just like union, except that we take the sum rather than the max.
(define (dyadic-sob-sum! result sob1 sob2)
(let ((sob1-ht (sob-hash-table sob1))
(sob2-ht (sob-hash-table sob2))
(result-ht (sob-hash-table result)))
(hash-table-for-each
(lambda (key value1)
(let ((value2 (hash-table-ref/default sob2-ht key 0)))
(hash-table-set! result-ht key (+ value1 value2))))
sob1-ht)
(hash-table-for-each
(lambda (key value2)
(let ((value1 (hash-table-ref/default sob1-ht key 0)))
(if (= value1 0)
(hash-table-set! result-ht key value2))))
sob2-ht)))
;; Sum is defined for bags only; for sets, it is the same as union.
(define (bag-sum . bags)
(check-all-bags bags)
(apply sob-sum bags))
(define (sob-sum! sob1 . sobs)
(for-each
(lambda (sob) (dyadic-sob-sum! sob1 sob1 sob))
sobs)
sob1)
(define (bag-sum! . bags)
(check-all-bags bags)
(apply sob-sum! bags))
;; For xor exactly two arguments are required, so the above structures are
;; not necessary. This version accepts a result sob and computes the
;; absolute difference between the counts in the first sob and the
;; corresponding counts in the second.
;; We start by copying the entries in the second sob but not the first
;; into the first. Then we scan the first sob, computing the absolute
;; difference of the values and writing them back into the first sob.
;; It's essential to scan the second sob first, as we are not going to
;; damage it in the process. (Hat tip: Sam Tobin-Hochstadt.)
(define (sob-xor! result sob1 sob2)
(let ((sob1-ht (sob-hash-table sob1))
(sob2-ht (sob-hash-table sob2))
(result-ht (sob-hash-table result)))
(hash-table-for-each
(lambda (key value2)
(let ((value1 (hash-table-ref/default sob1-ht key 0)))
(if (= value1 0)
(hash-table-set! result-ht key value2))))
sob2-ht)
(hash-table-for-each
(lambda (key value1)
(let ((value2 (hash-table-ref/default sob2-ht key 0)))
(hash-table-set! result-ht key (abs (- value1 value2)))))
sob1-ht)
(sob-cleanup! result)))
(define (set-xor set1 set2)
(check-set set1)
(check-set set2)
(check-same-comparator set1 set2)
(sob-xor! (sob-empty-copy set1) set1 set2))
(define (bag-xor bag1 bag2)
(check-bag bag1)
(check-bag bag2)
(check-same-comparator bag1 bag2)
(sob-xor! (sob-empty-copy bag1) bag1 bag2))
(define (set-xor! set1 set2)
(check-set set1)
(check-set set2)
(check-same-comparator set1 set2)
(sob-xor! set1 set1 set2))
(define (bag-xor! bag1 bag2)
(check-bag bag1)
(check-bag bag2)
(check-same-comparator bag1 bag2)
(sob-xor! bag1 bag1 bag2))
;;; A few bag-specific procedures
(define (sob-product! n result sob)
(let ((rht (sob-hash-table result)))
(hash-table-for-each
(lambda (elem count) (hash-table-set! rht elem (* count n)))
(sob-hash-table sob))
result))
(define (valid-n n)
(and (integer? n) (exact? n) (positive? n)))
(define (bag-product n bag)
(check-bag bag)
(valid-n n)
(sob-product! n (sob-empty-copy bag) bag))
(define (bag-product! n bag)
(check-bag bag)
(valid-n n)
(sob-product! n bag bag))
(define (bag-unique-size bag)
(check-bag bag)
(hash-table-size (sob-hash-table bag)))
(define (bag-element-count bag elem)
(check-bag bag)
(hash-table-ref/default (sob-hash-table bag) elem 0))
(define (bag-for-each-unique proc bag)
(check-bag bag)
(hash-table-for-each
(lambda (key value) (proc key value))
(sob-hash-table bag)))
(define (bag-fold-unique proc nil bag)
(check-bag bag)
(let ((result nil))
(hash-table-for-each
(lambda (elem count) (set! result (proc elem count result)))
(sob-hash-table bag))
result))
(define (bag->set bag)
(check-bag bag)
(let ((result (make-sob (sob-comparator bag) #f)))
(hash-table-for-each
(lambda (key value) (sob-increment! result key value))
(sob-hash-table bag))
result))
(define (set->bag set)
(check-set set)
(let ((result (make-sob (sob-comparator set) #t)))
(hash-table-for-each
(lambda (key value) (sob-increment! result key value))
(sob-hash-table set))
result))
(define (set->bag! bag set)
(check-bag bag)
(check-set set)
(check-same-comparator set bag)
(hash-table-for-each
(lambda (key value) (sob-increment! bag key value))
(sob-hash-table set))
bag)
(define (bag->alist bag)
(check-bag bag)
(bag-fold-unique
(lambda (elem count list) (cons (cons elem count) list))
'()
bag))
(define (alist->bag comparator alist)
(let* ((result (bag comparator))
(ht (sob-hash-table result)))
(for-each
(lambda (assoc)
(let ((element (car assoc)))
(if (not (hash-table-contains? ht element))
(sob-increment! result element (cdr assoc)))))
alist)
result))
;;; Comparators
;; Hash over sobs
(define (sob-hash sob)
(let* ((ht (sob-hash-table sob))
(hash (comparator-hash-function (sob-comparator sob))))
(sob-fold
(lambda (element result) (+ (hash element) result))
5381
sob)))
;; Set and bag comparator
(define set-comparator (make-comparator set? set=? #f sob-hash))
(define bag-comparator (make-comparator bag? bag=? #f sob-hash))
;;; Set/bag printer (for debugging)
(define (sob-print sob port)
(display (if (sob-multi? sob) "&bag[" "&set[") port)
(sob-for-each
(lambda (elem) (display " " port) (write elem port))
sob)
(display " ]" port))
;;; Register above comparators for use by default-comparator
(comparator-register-default! set-comparator)
(comparator-register-default! bag-comparator)
))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a115.sls 0000664 0000000 0000000 00000000762 14737542645 0020030 0 ustar 00root root 0000000 0000000 (library (srfi :115)
(export regexp regexp? valid-sre? rx regexp->sre char-set->sre
regexp-matches regexp-matches? regexp-search
regexp-replace regexp-replace-all
regexp-fold regexp-extract regexp-split regexp-partition
regexp-match? regexp-match-count
regexp-match-submatch regexp-match-submatch/list
regexp-match-submatch-start regexp-match-submatch-end
regexp-match->list regexp-match->sexp)
(import (srfi :115 regexp)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a115/ 0000775 0000000 0000000 00000000000 14737542645 0017300 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a115/regexp-impl.scm 0000664 0000000 0000000 00000133702 14737542645 0022243 0 ustar 00root root 0000000 0000000 ;; regexp.scm -- simple non-bactracking NFA implementation
;; Copyright (c) 2013-2016 Alex Shinn. All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt
;;; An rx represents a start state and meta-info such as the number
;;; and names of submatches.
(define-record-type Rx
(make-rx start-state num-matches num-save-indexes non-greedy-indexes
match-rules match-names sre)
regexp?
(start-state rx-start-state rx-start-state-set!)
(num-matches rx-num-matches rx-num-matches-set!)
(num-save-indexes rx-num-save-indexes rx-num-save-indexes-set!)
(non-greedy-indexes rx-non-greedy-indexes rx-non-greedy-indexes-set!)
(match-rules rx-rules rx-rules-set!)
(match-names rx-names rx-names-set!)
(sre regexp->sre))
;; Syntactic sugar.
(define-syntax rx
(syntax-rules ()
((rx sre ...)
(regexp `(: sre ...)))))
;;; A state is a single nfa state with transition rules.
(define-record-type State
(%make-state accept? chars match match-rule next1 next2 id)
state?
;; A boolean indicating if this is an accepting state.
(accept? state-accept? state-accept?-set!)
;; A char or char-set indicating when we can transition.
;; Alternately, #f indicates an epsilon transition, while a
;; procedure of the form (lambda (ch i matches) ...) is a predicate
;; which should return #t if the char matches.
(chars state-chars state-chars-set!)
;; A single integer indicating the match position to record.
(match state-match state-match-set!)
;; The rule for merging ambiguous matches. Can be any of: left,
;; right, (list i j). Posix semantics are equivalent to using left
;; for the beginning of a submatch and right for the end. List is
;; used to capture a list of submatch data in the current match.
(match-rule state-match-rule state-match-rule-set!)
;; The destination if the char match succeeds.
(next1 state-next1 state-next1-set!)
;; An optional additional transition used for forking to two states.
(next2 state-next2 state-next2-set!)
;; A unique (per regexp) id for debugging.
(id state-id))
(define (make-state accept? chars match match-rule next1 next2 id)
(if (and next1 (not (state? next1)))
(error "expected a state" next1))
(if (and next2 (not (state? next2)))
(error "expected a state" next2))
(%make-state accept? chars match match-rule next1 next2 id))
(define ~none 0)
(define ~ci? 1)
(define ~ascii? 2)
(define ~nocapture? 4)
(define (flag-set? flags i) (= i (bitwise-and flags i)))
(define (flag-join a b) (if b (bitwise-ior a b) a))
(define (flag-clear a b) (bitwise-and a (bitwise-not b)))
(define (char-set-ci cset)
(char-set-fold
(lambda (ch res)
(char-set-adjoin! (char-set-adjoin! res (char-upcase ch))
(char-downcase ch)))
(char-set)
cset))
(define (make-char-state ch flags next id)
(if (flag-set? flags ~ci?)
(let ((cset (cond ((char? ch) (char-set-ci (char-set ch)))
((char-set? ch) (char-set-ci ch))
(else ch))))
(make-state #f cset #f #f next #f id))
(make-state #f ch #f #f next #f id)))
(define (make-fork-state next1 next2 id)
(make-state #f #f #f #f next1 next2 id))
(define (make-epsilon-state next id)
(make-fork-state next #f id))
(define (make-accept-state id)
(make-state #t #f #f #f #f #f id))
;; A record holding the current match data - essentially a wrapper
;; around a vector, plus a reference to the RX for meta-info.
(define-record-type Regexp-Match
(%make-regexp-match matches rx string)
regexp-match?
(matches regexp-match-matches regexp-match-matches-set!)
(rx regexp-match-rx)
(string regexp-match-string))
(define (regexp-match-rules md)
(rx-rules (regexp-match-rx md)))
(define (regexp-match-names md)
(rx-names (regexp-match-rx md)))
(define (make-regexp-match len rx str)
(%make-regexp-match (make-vector len #f) rx str))
(define (make-regexp-match-for-rx rx str)
(make-regexp-match (rx-num-save-indexes rx) rx str))
(define (regexp-match-count md)
(- (quotient (vector-length (regexp-match-matches md)) 2) 1))
(define (regexp-match-name-offset md name)
(let lp ((ls (regexp-match-names md)) (first #f))
(cond
((null? ls) (or first (error "unknown match name" md name)))
((eq? name (caar ls))
(if (regexp-match-submatch-start+end md (cdar ls))
(cdar ls)
(lp (cdr ls) (or first (cdar ls)))))
(else (lp (cdr ls) first)))))
(define (regexp-match-ref md n)
(vector-ref (regexp-match-matches md)
(if (integer? n)
n
(regexp-match-name-offset md n))))
(define (regexp-match-set! md n val)
(vector-set! (regexp-match-matches md) n val))
(define (copy-regexp-match md)
(let* ((src (regexp-match-matches md))
(len (vector-length src))
(dst (make-vector len #f)))
(do ((i 0 (+ i 1)))
((= i len)
(%make-regexp-match dst (regexp-match-rx md) (regexp-match-string md)))
(vector-set! dst i (vector-ref src i)))))
;;> Returns the matching result for the given named or indexed
;;> submatch \var{n}, possibly as a list for a submatch-list, or
;;> \scheme{#f} if not matched.
(define (regexp-match-submatch/list md n)
(let ((n (if (integer? n) n (regexp-match-name-offset md n))))
(cond
((>= n (vector-length (regexp-match-rules md)))
#f)
(else
(let ((rule (vector-ref (regexp-match-rules md) n)))
(cond
((pair? rule)
(let ((start (regexp-match-ref md (car rule)))
(end (regexp-match-ref md (cdr rule)))
(str (regexp-match-string md)))
(and start end (substring-cursor str start end))))
(else
(let ((res (regexp-match-ref md rule)))
(if (pair? res)
(reverse res)
res)))))))))
;;> Returns the matching substring for the given named or indexed
;;> submatch \var{n}, or \scheme{#f} if not matched.
(define (regexp-match-submatch md n)
(let ((res (regexp-match-submatch/list md n)))
(if (pair? res) (car res) res)))
(define (regexp-match-submatch-start+end md n)
(let ((n (if (integer? n) n (regexp-match-name-offset md n))))
(and (< n (vector-length (regexp-match-rules md)))
(let ((rule (vector-ref (regexp-match-rules md) n)))
(if (pair? rule)
(let ((start (regexp-match-ref md (car rule)))
(end (regexp-match-ref md (cdr rule)))
(str (regexp-match-string md)))
(and start end
(cons (string-cursor->index str start)
(string-cursor->index str end))))
#f)))))
;;> Returns the start index for the given named or indexed submatch
;;> \var{n}, or \scheme{#f} if not matched.
(define (regexp-match-submatch-start md n)
(cond ((regexp-match-submatch-start+end md n) => car) (else #f)))
;;> Returns the end index for the given named or indexed submatch
;;> \var{n}, or \scheme{#f} if not matched.
(define (regexp-match-submatch-end md n)
(cond ((regexp-match-submatch-start+end md n) => cdr) (else #f)))
(define (regexp-match-convert recurse? md str)
(cond
((vector? md)
(let lp ((i 0) (res '()))
(cond
((>= i (vector-length md))
(reverse res))
((string-cursor? (vector-ref md i))
(lp (+ i 2)
(cons (substring-cursor str
(vector-ref md i)
(vector-ref md (+ i 1)))
res)))
(else
(lp (+ i 1)
(cons (regexp-match-convert recurse? (vector-ref md i) str)
res))))))
((list? md)
(if recurse?
(map (lambda (x) (regexp-match-convert recurse? x str)) (reverse md))
(regexp-match-convert recurse? (car md) str)))
((and (pair? md) (string-cursor? (car md)) (string-cursor? (cdr md)))
(substring-cursor str (car md) (cdr md)))
((regexp-match? md)
(regexp-match-convert
recurse? (regexp-match-matches md) (regexp-match-string md)))
(else
md)))
;;> Convert an regexp-match result to a list of submatches, beginning
;;> with the full match, using \scheme{#f} for unmatched submatches.
(define (regexp-match->list md)
(regexp-match-convert #f md #f))
;;> Convert an regexp-match result to a forest of submatches, beginning
;;> with the full match, using \scheme{#f} for unmatched submatches.
(define (regexp-match->sexp md)
(regexp-match-convert #t md #f))
;; Collect results from a list match.
(define (match-collect md spec)
(define (match-extract md n)
(let* ((vec (regexp-match-matches md))
(rules (regexp-match-rules md))
(n-rule (vector-ref rules n))
(rule (vector-ref rules n-rule)))
(if (pair? rule)
(let ((start (regexp-match-ref md (car rule)))
(end (regexp-match-ref md (cdr rule))))
(and start end (cons start end)))
(regexp-match-ref md rule))))
(let ((end (cadr spec))
(vec (regexp-match-matches md)))
(let lp ((i (+ 1 (car spec)))
(ls '()))
(if (>= i end)
(reverse ls)
(lp (+ i 1) (cons (match-extract md i) ls))))))
;; A searcher represents a single rx state and match information.
(define-record-type Searcher
(make-searcher state matches)
searcher?
(state searcher-state searcher-state-set!)
(matches searcher-matches searcher-matches-set!))
;; Merge two regexp-matches, preferring the leftmost-longest of their
;; matches, or shortest for non-greedy matches.
(define (regexp-match>=? m1 m2)
(let ((non-greedy-indexes (rx-non-greedy-indexes (regexp-match-rx m1)))
(end (- (vector-length (regexp-match-matches m1)) 1)))
(let lp ((i 0))
(cond
((>= i end)
#t)
((and (eqv? (regexp-match-ref m1 i)
(regexp-match-ref m2 i))
(eqv? (regexp-match-ref m1 (+ i 1))
(regexp-match-ref m2 (+ i 1))))
(lp (+ i 2)))
(else
(not
(and (string-cursor? (regexp-match-ref m2 i))
(or (not (string-cursor? (regexp-match-ref m1 i)))
(string-cursor (regexp-match-ref m2 i)
(regexp-match-ref m1 i))
;; sanity check for incompletely advanced epsilons
(and (string-cursor? (regexp-match-ref m1 (+ i 1)))
(string-cursor (regexp-match-ref m1 (+ i 1))
(regexp-match-ref m1 i)))
((if (memq (+ i 1) non-greedy-indexes) not values)
(and
(string-cursor=? (regexp-match-ref m2 i)
(regexp-match-ref m1 i))
(or (not (string-cursor? (regexp-match-ref m2 (+ i 1))))
(and (string-cursor? (regexp-match-ref m1 (+ i 1)))
(string-cursor>?
(regexp-match-ref m2 (+ i 1))
(regexp-match-ref m1 (+ i 1)))))))))))))))
(define (regexp-match-max m1 m2)
(if (regexp-match>=? m1 m2) m1 m2))
;; Merge match data from sr2 into sr1, preferring the leftmost-longest
;; match in the event of a conflict.
(define (searcher-merge! sr1 sr2)
(let ((m (regexp-match-max (searcher-matches sr1) (searcher-matches sr2))))
(if (not (eq? m (searcher-matches sr1)))
(searcher-matches-set! sr1 (copy-regexp-match m)))))
(define (searcher-max sr1 sr2)
(if (or (not (searcher? sr2))
(regexp-match>=? (searcher-matches sr1) (searcher-matches sr2)))
sr1
sr2))
(define (searcher-start-match sr)
(regexp-match-ref (searcher-matches sr) 0))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; A posse is a group of searchers.
(define (make-posse . o)
(make-hash-table eq?))
(define posse? hash-table?)
(define (posse-empty? posse) (zero? (hash-table-size posse)))
(define (posse-ref posse sr)
(hash-table-ref/default posse (searcher-state sr) #f))
(define (posse-add! posse sr)
(hash-table-set! posse (searcher-state sr) sr))
(define (posse-clear! posse)
(hash-table-walk posse (lambda (key val) (hash-table-delete! posse key))))
(define (posse-for-each proc posse)
(hash-table-walk posse (lambda (key val) (proc val))))
(define (posse-every pred posse)
(hash-table-fold posse (lambda (key val acc) (and acc (pred val))) #t))
(define (posse->list posse)
(hash-table-values posse))
(define (list->posse ls)
(let ((searchers (make-posse)))
(for-each (lambda (sr) (posse-add! searchers sr)) ls)
searchers))
(define (posse . args)
(list->posse args))
(define (make-start-searcher rx str)
(make-searcher (rx-start-state rx) (make-regexp-match-for-rx rx str)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Execution
;; A transition which doesn't advance the index.
(define (epsilon-state? st)
(or (not (state-chars st))
(procedure? (state-chars st))))
;; Match the state against a char and index.
(define (state-matches? st str i ch start end matches)
(let ((matcher (state-chars st)))
(cond
((char? matcher)
(eqv? matcher ch))
((char-set? matcher)
(char-set-contains? matcher ch))
((pair? matcher)
(and (char<=? (car matcher) ch) (char<=? ch (cdr matcher))))
((procedure? matcher)
(matcher str i ch start end matches))
((not matcher))
(else
(error "unknown state matcher" (state-chars st))))))
;; Advance epsilons together - if the State is newly added to the
;; group and is an epsilon state, recursively add the transition.
(define (posse-advance! new seen accept sr str i start end)
(let advance! ((sr sr))
(let ((st (searcher-state sr)))
;; Update match data.
(cond
((state-match st)
(let* ((index (state-match st))
(matches (searcher-matches sr))
(before (copy-regexp-match matches)))
(cond
((pair? index)
;; Submatch list, accumulate and push.
(let* ((prev (regexp-match-ref matches (car index)))
(new (cons (match-collect matches (cdr index))
(if (pair? prev) prev '()))))
(regexp-match-set! matches (car index) new)))
((not (and (eq? 'non-greedy-left (state-match-rule st))
(regexp-match-ref matches index)
(string-cursor>=? (regexp-match-ref matches index)
(regexp-match-ref matches (- index 1)))))
(regexp-match-set! matches index i))))))
;; Follow transitions.
(cond
((state-accept? st)
(set-cdr! accept (searcher-max sr (cdr accept))))
((posse-ref seen sr)
=> (lambda (sr-prev) (searcher-merge! sr-prev sr)))
((epsilon-state? st)
(let ((ch (and (string-cursor i end) (string-cursor-ref str i))))
;; Epsilon transition. If there is a procedure matcher,
;; it's a guarded epsilon and needs to be checked.
(cond
((state-matches? st str i ch start end (searcher-matches sr))
(posse-add! seen sr)
(let* ((next1 (state-next1 st))
(next2 (state-next2 st))
(matches
(and next2 (searcher-matches sr))))
(cond
(next1
(searcher-state-set! sr next1)
(advance! (make-searcher next1 (copy-regexp-match (searcher-matches sr))))))
(cond
(next2
(let ((sr2 (make-searcher next2 (copy-regexp-match matches))))
(advance! sr2)))))))))
;; Non-special, non-epsilon searcher, add to posse.
((posse-ref new sr)
;; Merge regexp-match for existing searcher.
=> (lambda (sr-prev) (searcher-merge! sr-prev sr)))
(else
;; Add new searcher.
(posse-add! new sr))))))
;; Run so long as there is more to match.
(define (regexp-run-offsets search? rx str start end)
(let ((rx (regexp rx))
(epsilons (posse))
(accept (list #f)))
(let lp ((i start)
(searchers1 (posse))
(searchers2 (posse)))
;; Advance initial epsilons once from the first index, or every
;; time when searching.
(cond
((or search? (string-cursor=? i start))
(posse-advance! searchers1 epsilons accept (make-start-searcher rx str)
str i start end)
(posse-clear! epsilons)))
(cond
((or (string-cursor>=? i end)
(and search?
(searcher? (cdr accept))
(let ((accept-start (searcher-start-match (cdr accept))))
(posse-every
(lambda (searcher)
(string-cursor>? (searcher-start-match searcher)
accept-start))
searchers1)))
(and (not search?)
(posse-empty? searchers1)))
;; Terminate when the string is done or there are no more
;; searchers. If we terminate prematurely and are not
;; searching, return false.
(and (searcher? (cdr accept))
(let ((matches (searcher-matches (cdr accept))))
(and (or search? (string-cursor>=? (regexp-match-ref matches 1)
end))
(searcher-matches (cdr accept))))))
(else
;; Otherwise advance normally.
(let ((ch (string-cursor-ref str i))
(i2 (string-cursor-next str i)))
(posse-for-each ;; NOTE: non-deterministic from hash order
(lambda (sr)
(cond
((state-matches? (searcher-state sr) str i ch
start end (searcher-matches sr))
(searcher-state-set! sr (state-next1 (searcher-state sr)))
;; Epsilons are considered at the next position.
(posse-advance! searchers2 epsilons accept sr str i2 start end)
(posse-clear! epsilons))))
searchers1)
(posse-clear! searchers1)
(lp i2 searchers2 searchers1)))))))
;; Wrapper to determine start and end offsets.
(define (regexp-run search? rx str . o)
(let ((start (string-start-arg str o))
(end (string-end-arg str (if (pair? o) (cdr o) o))))
(regexp-run-offsets search? rx str start end)))
;;> Match the given regexp or SRE against the entire string and return
;;> the match data on success. Returns \scheme{#f} on failure.
(define (regexp-matches rx str . o)
(apply regexp-run #f rx str o))
;;> Match the given regexp or SRE against the entire string and return
;;> the \scheme{#t} on success. Returns \scheme{#f} on failure.
(define (regexp-matches? rx str . o)
(and (apply regexp-matches rx str o) #t))
;;> Search for the given regexp or SRE within string and return
;;> the match data on success. Returns \scheme{#f} on failure.
(define (regexp-search rx str . o)
(apply regexp-run #t rx str o))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Compiling
(define (parse-flags ls)
(define (symbol->flag s)
(case s ((i ci case-insensitive) ~ci?) (else ~none)))
(let lp ((ls ls) (res ~none))
(if (not (pair? ls))
res
(lp (cdr ls) (flag-join res (symbol->flag (car ls)))))))
(define char-set:nonl
(char-set-difference char-set:full (char-set #\newline)))
(define char-set:control (ucs-range->char-set 0 32))
(define char-set:word-constituent
(char-set-union char-set:letter char-set:digit (char-set #\_)))
(define %char-set:word-constituent
(char-set-union %char-set:letter %char-set:digit (char-set #\_)))
(define (char-word-constituent? ch)
(char-set-contains? char-set:word-constituent ch))
(define get-char-set:cased
(let ((char-set:cased #f))
(lambda ()
(if (not char-set:cased)
(set! char-set:cased
(char-set-union char-set:upper-case
char-set:lower-case
char-set:title-case)))
char-set:cased)))
(define (match/bos str i ch start end matches)
(string-cursor=? i start))
(define (match/eos str i ch start end matches)
(string-cursor>=? i end))
(define (match/bol str i ch start end matches)
(or (string-cursor=? i start)
(eqv? #\newline (string-cursor-ref str (string-cursor-prev str i)))))
(define (match/eol str i ch start end matches)
(or (string-cursor>=? i end)
(eqv? #\newline (string-cursor-ref str i))))
(define (match/bow str i ch start end matches)
(and (string-cursor i end)
(or (string-cursor=? i start)
(not (char-word-constituent?
(string-cursor-ref str (string-cursor-prev str i)))))
(char-word-constituent? ch)))
(define (match/eow str i ch start end matches)
(and (or (string-cursor>=? i end)
(not (char-word-constituent? ch)))
(string-cursor>? i start)
(char-word-constituent?
(string-cursor-ref str (string-cursor-prev str i)))))
(define (match/nwb str i ch start end matches)
(and (not (match/bow str i ch start end matches))
(not (match/eow str i ch start end matches))))
(define (match/bog str i ch start end matches)
(and (string-cursor i end)
(or (string-cursor=? i start)
(match/eog str (string-cursor-prev str i) ch start end matches))))
(define (match/eog str i ch start end matches)
(and (string-cursor>? i start)
(or (string-cursor>=? i end)
(let ((m (regexp-search re:grapheme str
(string-cursor->index str i)
(string-cursor->index str end))))
(and m (string-cursor<=? (regexp-match-submatch-end m 0) i))))))
(define (lookup-char-set name flags)
(cond
((flag-set? flags ~ascii?)
(case name
((any) char-set:full)
((nonl) char-set:nonl)
((lower-case lower)
(if (flag-set? flags ~ci?) %char-set:letter %char-set:lower-case))
((upper-case upper)
(if (flag-set? flags ~ci?) %char-set:letter %char-set:upper-case))
((title-case title)
(if (flag-set? flags ~ci?) %char-set:letter (char-set)))
((alphabetic alpha) %char-set:letter)
((numeric num digit) %char-set:digit)
((alphanumeric alphanum alnum) %char-set:letter+digit)
((punctuation punct) %char-set:punctuation)
((symbol) %char-set:symbol)
((graphic graph) %char-set:graphic)
((word-constituent) %char-set:word-constituent)
((whitespace white space) %char-set:whitespace)
((printing print) %char-set:printing)
((control cntrl) %char-set:iso-control)
((hex-digit xdigit hex) char-set:hex-digit)
((ascii) char-set:ascii)
(else #f)))
(else
(case name
((any) char-set:full)
((nonl) char-set:nonl)
((lower-case lower)
(if (flag-set? flags ~ci?) (get-char-set:cased) char-set:lower-case))
((upper-case upper)
(if (flag-set? flags ~ci?) (get-char-set:cased) char-set:upper-case))
((title-case title)
(if (flag-set? flags ~ci?) (get-char-set:cased) char-set:title-case))
((alphabetic alpha) char-set:letter)
((numeric num digit) char-set:digit)
((alphanumeric alphanum alnum) char-set:letter+digit)
((punctuation punct) char-set:punctuation)
((symbol) char-set:symbol)
((graphic graph) char-set:graphic)
((word-constituent) char-set:word-constituent)
((whitespace white space) char-set:whitespace)
((printing print) char-set:printing)
((control cntrl) char-set:control)
((hex-digit xdigit hex) char-set:hex-digit)
((ascii) char-set:ascii)
(else #f)))))
(define (sre-flatten-ranges orig-ls)
(let lp ((ls orig-ls) (res '()))
(cond
((null? ls)
(reverse res))
((string? (car ls))
(lp (append (string->list (car ls)) (cdr ls)) res))
((null? (cdr ls))
(error "unbalanced cset / range" orig-ls))
((string? (cadr ls))
(lp (cons (car ls) (append (string->list (cadr ls)) (cddr ls))) res))
(else
(lp (cddr ls) (cons (cons (car ls) (cadr ls)) res))))))
(define (every pred ls)
(or (null? ls) (and (pred (car ls)) (every pred (cdr ls)))))
(define (char-set-sre? sre)
(or (char? sre)
(and (string? sre) (= 1 (string-length sre)))
(lookup-char-set sre ~none)
(and (pair? sre)
(or (string? (car sre))
(memq (car sre)
'(char-set / char-range & and ~ complement - difference))
(and (memq (car sre)
'(\x7C;
or w/case w/nocase w/unicode w/ascii))
(every char-set-sre? (cdr sre)))))))
(define (non-greedy-sre? sre)
(and (pair? sre)
(or (memq (car sre) '(?? *? **? non-greedy-optional
non-greedy-zero-or-more non-greedy-repeated))
(and (memq (car sre) '(: seq w/case w/nocase w/unicode w/ascii))
(non-greedy-sre? (car (reverse sre))))
(and (eq? (car sre) 'or)
(any non-greedy-sre? (cdr sre))))))
(define (valid-sre? x)
(guard (exn (else #f)) (regexp x) #t))
(define (sre->char-set sre . o)
(let ((flags (if (pair? o) (car o) ~none)))
(define (->cs sre) (sre->char-set sre flags))
(define (maybe-ci sre)
(if (flag-set? flags ~ci?) (char-set-ci sre) sre))
(cond
((lookup-char-set sre flags))
((char-set? sre) (maybe-ci sre))
((char? sre) (maybe-ci (char-set sre)))
((string? sre)
(if (= 1 (string-length sre))
(maybe-ci (string->char-set sre))
(error "only single char strings can be char-sets")))
((pair? sre)
(if (string? (car sre))
(maybe-ci (string->char-set (car sre)))
(case (car sre)
((char-set) (maybe-ci (string->char-set (cadr sre))))
((/ char-range)
(->cs
`(or ,@(map (lambda (x)
(ucs-range->char-set
(char->integer (car x))
(+ 1 (char->integer (cdr x)))))
(sre-flatten-ranges (cdr sre))))))
((& and) (apply char-set-intersection (map ->cs (cdr sre))))
((\x7C;
or) (apply char-set-union (map ->cs (cdr sre))))
((~ complement) (char-set-complement (->cs `(or ,@(cdr sre)))))
((- difference) (char-set-difference (->cs (cadr sre))
(->cs `(or ,@(cddr sre)))))
((w/case) (sre->char-set (cadr sre) (flag-clear flags ~ci?)))
((w/nocase) (sre->char-set (cadr sre) (flag-join flags ~ci?)))
((w/ascii) (sre->char-set (cadr sre) (flag-join flags ~ascii?)))
((w/unicode) (sre->char-set (cadr sre) (flag-clear flags ~ascii?)))
(else (error "invalid sre char-set" sre)))))
(else (error "invalid sre char-set" sre)))))
(define (char-set->sre cset)
(list (char-set->string cset)))
(define (strip-submatches sre)
(if (pair? sre)
(case (car sre)
(($ submatch) (strip-submatches (cons ': (cdr sre))))
((-> => submatch-named) (strip-submatches (cons ': (cddr sre))))
(else (cons (strip-submatches (car sre))
(strip-submatches (cdr sre)))))
sre))
(define (sre-expand-reps from to sre)
(let ((sre0 (strip-submatches sre)))
(let lp ((i 0) (res '(:)))
(if (= i from)
(cond
((not to)
(reverse (cons `(* ,sre) res)))
((= from to)
(reverse (cons sre (cdr res))))
(else
(let lp ((i (+ i 1)) (res res))
(if (>= i to)
(reverse (cons `(? ,sre) res))
(lp (+ i 1) (cons `(? ,sre0) res))))))
(lp (+ i 1) (cons sre0 res))))))
;;> Compile an \var{sre} into a regexp.
(define (regexp sre . o)
(define current-index 2)
(define current-match 0)
(define current-id 0)
(define match-names '())
(define match-rules (list (cons 0 1)))
(define non-greedy-indexes '())
(define (next-id)
(let ((res current-id)) (set! current-id (+ current-id 1)) res))
(define (make-submatch-state sre flags next index)
(let* ((n3 (make-epsilon-state next (next-id)))
(n2 (->rx sre flags n3))
(n1 (make-epsilon-state n2 (next-id)))
(non-greedy? (non-greedy-sre? sre)))
(state-match-set! n1 index)
(state-match-rule-set! n1 'left)
(state-match-set! n3 (+ index 1))
(state-match-rule-set! n3 (if non-greedy? 'non-greedy-left 'right))
(if non-greedy?
(set! non-greedy-indexes (cons (+ index 1) non-greedy-indexes)))
n1))
(define (->rx sre flags next)
(cond
;; The base cases chars and strings match literally.
((char? sre)
(make-char-state sre flags next (next-id)))
((char-set? sre)
(make-char-state sre flags next (next-id)))
((string? sre)
(->rx (cons 'seq (string->list sre)) flags next))
((and (symbol? sre) (lookup-char-set sre flags))
=> (lambda (cset) (make-char-state cset ~none next (next-id))))
((symbol? sre)
(case sre
((epsilon) next)
((bos) (make-char-state match/bos flags next (next-id)))
((eos) (make-char-state match/eos flags next (next-id)))
((bol) (make-char-state match/bol flags next (next-id)))
((eol) (make-char-state match/eol flags next (next-id)))
((bow) (make-char-state match/bow flags next (next-id)))
((eow) (make-char-state match/eow flags next (next-id)))
((nwb) (make-char-state match/nwb flags next (next-id)))
((bog) (make-char-state match/bog flags next (next-id)))
((eog) (make-char-state match/eog flags next (next-id)))
((grapheme)
(->rx
`(or (: (* ,char-set:hangul-l) (+ ,char-set:hangul-v)
(* ,char-set:hangul-t))
(: (* ,char-set:hangul-l) ,char-set:hangul-v
(* ,char-set:hangul-v) (* ,char-set:hangul-t))
(: (* ,char-set:hangul-l) ,char-set:hangul-lvt
(* ,char-set:hangul-t))
(+ ,char-set:hangul-l)
(+ ,char-set:hangul-t)
(+ ,char-set:regional-indicator)
(: "\r\n")
(: (~ control ("\r\n"))
(+ ,char-set:extend-or-spacing-mark))
control)
flags
next))
((word) (->rx '(word+ any) flags next))
(else (error "unknown sre" sre))))
((pair? sre)
(case (car sre)
((seq :)
;; Sequencing. An empty sequence jumps directly to next,
;; otherwise we join the first element to the sequence formed
;; of the remaining elements followed by next.
(if (null? (cdr sre))
next
;; Make a dummy intermediate to join the states so that
;; we can generate n1 first, preserving the submatch order.
(let* ((n2 (make-epsilon-state #f (next-id)))
(n1 (->rx (cadr sre) flags n2))
(n3 (->rx (cons 'seq (cddr sre)) flags next)))
(state-next1-set! n2 n3)
n1)))
((or \x7C;
)
;; Alternation. An empty alternation always fails.
;; Otherwise we fork between any of the alternations, each
;; continuing to next.
(cond
((null? (cdr sre))
#f)
((char-set-sre? sre)
(make-char-state (sre->char-set sre) flags next (next-id)))
((null? (cddr sre))
(->rx (cadr sre) flags next))
(else
(let* ((n1 (->rx (cadr sre) flags next))
(n2 (->rx (cons 'or (cddr sre)) flags next)))
(make-fork-state n1 n2 (next-id))))))
((? optional ?? non-greedy-optional)
;; Optionality. Either match the body or fork to the next
;; state directly.
(make-fork-state (->rx (cons 'seq (cdr sre)) flags next)
next (next-id)))
((* zero-or-more *? non-greedy-zero-or-more)
;; Repetition. Introduce two fork states which can jump from
;; the end of the loop to the beginning and from the
;; beginning to the end (to skip the first iteration).
(let* ((n2 (make-fork-state next #f (next-id)))
(n1 (make-fork-state (->rx (cons 'seq (cdr sre)) flags n2)
n2 (next-id))))
(state-next2-set! n2 n1)
n1))
((+ one-or-more)
;; One-or-more repetition. Same as above but the first
;; transition is required so the rx is simpler - we only
;; need one fork from the end of the loop to the beginning.
(let* ((n2 (make-fork-state next #f (next-id)))
(n1 (->rx (cons 'seq (cdr sre)) flags n2)))
(state-next2-set! n2 n1)
n1))
((= exactly)
;; Exact repetition.
(->rx (sre-expand-reps (cadr sre) (cadr sre) (cons 'seq (cddr sre)))
flags next))
((>= at-least)
;; n-or-more repetition.
(->rx (sre-expand-reps (cadr sre) #f (cons 'seq (cddr sre)))
flags next))
((** repeated **? non-greedy-repeated)
;; n-to-m repetition.
(->rx (sre-expand-reps (cadr sre) (car (cddr sre))
(cons 'seq (cdr (cddr sre))))
flags next))
((-> => submatch-named)
;; Named submatches just record the name for the current
;; match and rewrite as a non-named submatch.
(cond
((flag-set? flags ~nocapture?)
(->rx (cons 'seq (cddr sre)) flags next))
(else
(set! match-names
(cons (cons (cadr sre) (+ 1 current-match)) match-names))
(->rx (cons 'submatch (cddr sre)) flags next))))
((*-> *=> submatch-named-list)
(cond
((flag-set? flags ~nocapture?)
(->rx (cons 'seq (cddr sre)) flags next))
(else
(set! match-names (cons (cons (cadr sre) current-match) match-names))
(->rx (cons 'submatch-list (cddr sre)) flags next))))
(($ submatch)
;; A submatch wraps next with an epsilon transition before
;; next, setting the start and end index on the result and
;; wrapped next respectively.
(cond
((flag-set? flags ~nocapture?)
(->rx (cons 'seq (cdr sre)) flags next))
(else
(let ((num current-match)
(index current-index))
(set! current-match (+ current-match 1))
(set! current-index (+ current-index 2))
(set! match-rules `((,index . ,(+ index 1)) ,@match-rules))
(make-submatch-state (cons 'seq (cdr sre)) flags next index)))))
((*$ submatch-list)
;; A submatch-list wraps a range of submatch results into a
;; single match value.
(cond
((flag-set? flags ~nocapture?)
(->rx (cons 'seq (cdr sre)) flags next))
(else
(let* ((num current-match)
(index current-index))
(set! current-match (+ current-match 1))
(set! current-index (+ current-index 1))
(set! match-rules `(,index ,@match-rules))
(let* ((n2 (make-epsilon-state next (next-id)))
(n1 (->rx (cons 'submatch (cdr sre)) flags n2)))
(state-match-set! n2 (list index num current-match))
(state-match-rule-set! n2 'list)
n1)))))
((~ - & / complement difference and char-range char-set)
(make-char-state (sre->char-set sre flags) ~none next (next-id)))
((word)
(->rx `(: bow ,@(cdr sre) eow) flags next))
((word+)
(->rx `(word (+ ,(if (equal? '(any) (cdr sre))
'word-constituent
(char-set-intersection
char-set:word-constituent
(sre->char-set `(or ,@(cdr sre)) flags)))))
flags
next))
((w/case)
(->rx `(: ,@(cdr sre)) (flag-clear flags ~ci?) next))
((w/nocase)
(->rx `(: ,@(cdr sre)) (flag-join flags ~ci?) next))
((w/unicode)
(->rx `(: ,@(cdr sre)) (flag-clear flags ~ascii?) next))
((w/ascii)
(->rx `(: ,@(cdr sre)) (flag-join flags ~ascii?) next))
((w/nocapture)
(->rx `(: ,@(cdr sre)) (flag-join flags ~nocapture?) next))
(else
(if (string? (car sre))
(make-char-state (sre->char-set sre flags) ~none next (next-id))
(error "unknown sre" sre)))))))
(let ((flags (parse-flags (and (pair? o) (car o)))))
(if (regexp? sre)
sre
(let ((start (make-submatch-state
sre flags (make-accept-state (next-id)) 0)))
;; (define (state->list st)
;; (let ((seen (make-hash-table eq?))
;; (count 0))
;; (reverse
;; (let lp ((st st) (res '()))
;; (cond
;; ((not (state? st)) res)
;; ((hash-table-ref/default seen st #f) res)
;; (else
;; (hash-table-set! seen st count)
;; (let ((orig-count count))
;; (set! count (+ count 1))
;; (let* ((next1 (lp (state-next1 st) '()))
;; (next2 (lp (state-next2 st) '()))
;; (this (append
;; (list (state-id st) ;;orig-count
;; (cond
;; ((epsilon-state? st)
;; (if (state-chars st) '? '-))
;; ((and (char-set? (state-chars st))
;; (< (char-set-size (state-chars st)) 5))
;; (char-set->string (state-chars st)))
;; ((char? (state-chars st))
;; (string (state-chars st)))
;; (else '+))
;; (cond
;; ((state-next1 st) => state-id)
;; (else #f)))
;; (if (state-next2 st)
;; (list (state-id (state-next2 st)))
;; '())
;; (if (state-match st)
;; (list (list 'm (state-match st)))
;; '()))))
;; (append next2 next1 (cons this res))))))))))
;;(for-each (lambda (x) (write x) (newline)) (state->list start))
(make-rx start current-match current-index non-greedy-indexes
(list->vector (reverse match-rules)) match-names sre)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Utilities
;;> The fundamental regexp matching iterator. Repeatedly searches
;;> \var{str} for the regexp \var{re} so long as a match can be found.
;;> On each successful match, applies \scheme{(\var{kons} \var{i}
;;> \var{regexp-match} \var{str} \var{acc})} where \var{i} is the
;;> index since the last match (beginning with
;;> \var{start}),\var{regexp-match} is the resulting match, and
;;> \var{acc} is the result of the previous \var{kons} application,
;;> beginning with \var{knil}. When no more matches can be found,
;;> calls \var{finish} with the same arguments, except that
;;> \var{regexp-match} is \scheme{#f}.
;;>
;;> By default \var{finish} just returns \var{acc}.
(define (regexp-fold rx kons knil str . o)
(let* ((rx (regexp rx))
(finish (if (pair? o) (car o) (lambda (from md str acc) acc)))
(o (if (pair? o) (cdr o) o))
(start (string-start-arg str o))
(end (string-end-arg str (if (pair? o) (cdr o) o))))
(let lp ((i start)
(from start)
(acc knil))
(cond
((and (string-cursor i end) (regexp-run-offsets #t rx str i end))
=> (lambda (md)
(let ((j (regexp-match-ref md 1)))
(lp (if (and (string-cursor=? i j) (string-cursor j end))
(string-cursor-next str j)
j)
j
(kons (string-cursor->index str from) md str acc)))))
(else
(finish (string-cursor->index str from) #f str acc))))))
;;> Extracts all non-empty substrings of \var{str} which match
;;> \var{re} between \var{start} and \var{end} as a list of strings.
(define (regexp-extract rx str . o)
(apply regexp-fold
rx
(lambda (from md str a)
(let ((s (regexp-match-submatch md 0)))
(if (equal? s "") a (cons s a))))
'()
str
(lambda (from md str a) (reverse a))
o))
;;> Splits \var{str} into a list of strings separated by matches of
;;> \var{re}.
(define (regexp-split rx str . o)
;; start and end in indices passed to regexp-fold
(let ((start (if (pair? o) (car o) 0))
(end (if (and (pair? o) (pair? (cdr o))) (cadr o) (string-length str))))
(regexp-fold
rx
(lambda (from md str a)
(let ((i (regexp-match-submatch-start md 0))
(j (regexp-match-submatch-end md 0)))
(if (eqv? i j)
a
(cons j
(cons (substring str (car a) i) (cdr a))))))
(cons start '())
str
(lambda (from md str a)
(reverse (cons (substring str (car a) end) (cdr a))))
start
end)))
;;> Partitions \var{str} into a list of non-empty strings
;;> matching \var{re}, interspersed with the unmatched portions
;;> of the string. The first and every odd element is an unmatched
;;> substring, which will be the empty string if \var{re} matches
;;> at the beginning of the string or end of the previous match. The
;;> second and every even element will be a substring matching
;;> \var{re}. If the final match ends at the end of the string,
;;> no trailing empty string will be included. Thus, in the
;;> degenerate case where \var{str} is the empty string, the
;;> result is \scheme{("")}.
(define (regexp-partition rx str . o)
(let ((start (if (pair? o) (car o) 0))
(end (if (and (pair? o) (pair? (cdr o))) (cadr o) (string-length str))))
(define (kons from md str a)
(let ((i (regexp-match-submatch-start md 0))
(j (regexp-match-submatch-end md 0)))
(if (eqv? i j)
a
(let ((left (substring str (car a) i)))
(cons j
(cons (regexp-match-submatch md 0)
(cons left (cdr a))))))))
(define (final from md str a)
(if (or (< from end) (null? (cdr a)))
(cons (substring str (car a) end) (cdr a))
(cdr a)))
(reverse (regexp-fold rx kons (cons start '()) str final start end))))
;;> Returns a new string replacing the \var{count}th match of \var{re}
;;> in \var{str} the \var{subst}, where the zero-indexed \var{count}
;;> defaults to zero (i.e. the first match). If there are not
;;> \var{count} matches, returns the selected substring unmodified.
;;> \var{subst} can be a string, an integer or symbol indicating the
;;> contents of a numbered or named submatch of \var{re},\scheme{'pre}
;;> for the substring to the left of the match, or \scheme{'post} for
;;> the substring to the right of the match.
;;> The optional parameters \var{start} and \var{end} restrict both
;;> the matching and the substitution, to the given indices, such that
;;> the result is equivalent to omitting these parameters and
;;> replacing on \scheme{(substring str start end)}. As a convenience,
;;> a value of \scheme{#f} for \var{end} is equivalent to
;;> \scheme{(string-length str)}.
(define (regexp-replace rx str subst . o)
(let* ((start (if (and (pair? o) (car o)) (car o) 0))
(o (if (pair? o) (cdr o) '()))
(end (if (and (pair? o) (car o)) (car o) (string-length str)))
(o (if (pair? o) (cdr o) '()))
(count (if (pair? o) (car o) 0)))
(let lp ((i start) (count count))
(let ((m (regexp-search rx str i end)))
(cond
((not m) str)
((positive? count)
(lp (regexp-match-submatch-end m 0) (- count 1)))
(else
(string-concatenate
(cons
(substring str start (regexp-match-submatch-start m 0))
(append
(reverse (regexp-apply-match m str subst))
(list (substring str (regexp-match-submatch-end m 0) end)))))))))))
;;> Equivalent to \var{regexp-replace}, but replaces all occurrences
;;> of \var{re} in \var{str}.
(define (regexp-replace-all rx str subst . o)
(regexp-fold
rx
(lambda (i m str acc)
(let ((m-start (regexp-match-submatch-start m 0)))
(append (regexp-apply-match m str subst)
(if (>= i m-start)
acc
(cons (substring str i m-start) acc)))))
'()
str
(lambda (i m str acc)
(let ((end (string-length str)))
(string-concatenate-reverse
(if (>= i end)
acc
(cons (substring str i end) acc)))))))
(define (regexp-apply-match m str ls)
(let lp ((ls ls) (res '()))
(cond
((null? ls)
res)
((not (pair? ls))
(lp (list ls) res))
((integer? (car ls))
(lp (cdr ls) (cons (or (regexp-match-submatch m (car ls)) "") res)))
((procedure? (car ls))
(lp (cdr ls) (cons ((car ls) m) res)))
((symbol? (car ls))
(case (car ls)
((pre)
(lp (cdr ls)
(cons (substring str 0 (regexp-match-submatch-start m 0))
res)))
((post)
(lp (cdr ls)
(cons (substring str
(regexp-match-submatch-end m 0)
(string-length str))
res)))
(else
(cond
((assq (car ls) (regexp-match-names m))
=> (lambda (x) (lp (cons (cdr x) (cdr ls)) res)))
(else
(error "unknown match replacement" (car ls)))))))
(else
(lp (cdr ls) (cons (car ls) res))))))
(define re:grapheme (regexp 'grapheme))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a115/regexp.sls 0000664 0000000 0000000 00000010315 14737542645 0021315 0 ustar 00root root 0000000 0000000 #!r6rs
;; Copyright (c) 2009-2015 Alex Shinn
;; All rights reserved.
;;
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions
;; are met:
;; 1. Redistributions of source code must retain the above copyright
;; notice, this list of conditions and the following disclaimer.
;; 2. Redistributions in binary form must reproduce the above copyright
;; notice, this list of conditions and the following disclaimer in the
;; documentation and/or other materials provided with the distribution.
;; 3. The name of the author may not be used to endorse or promote products
;; derived from this software without specific prior written permission.
;;
;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;; Converted from lib/chibi/regexp.sld to R6RS by Göran Weinholt.
(library (srfi :115 regexp)
(export regexp regexp? valid-sre? rx regexp->sre char-set->sre
regexp-matches regexp-matches? regexp-search
regexp-replace regexp-replace-all
regexp-fold regexp-extract regexp-split regexp-partition
regexp-match? regexp-match-count
regexp-match-submatch regexp-match-submatch/list
regexp-match-submatch-start regexp-match-submatch-end
regexp-match->list regexp-match->sexp)
(import (rename (except (rnrs) define-record-type string-ci-hash string-hash
error)
(exists any))
(only (rnrs r5rs) quotient)
(rnrs mutable-pairs)
(srfi :9 records)
(srfi :14 char-sets)
(srfi :23 error)
(srfi :69 basic-hash-tables)
(srfi :115 regexp boundary)
(srfi private include))
(define %char-set:letter
(char-set-intersection char-set:ascii char-set:letter))
(define %char-set:lower-case
(char-set-intersection char-set:ascii char-set:lower-case))
(define %char-set:upper-case
(char-set-intersection char-set:ascii char-set:upper-case))
(define %char-set:digit
(char-set-intersection char-set:ascii char-set:digit))
(define %char-set:letter+digit
(char-set-intersection char-set:ascii char-set:letter+digit))
(define %char-set:punctuation
(char-set-intersection char-set:ascii char-set:punctuation))
(define %char-set:symbol
(char-set-intersection char-set:ascii char-set:symbol))
(define %char-set:graphic
(char-set-intersection char-set:ascii char-set:graphic))
(define %char-set:whitespace
(char-set-intersection char-set:ascii char-set:whitespace))
(define %char-set:printing
(char-set-intersection char-set:ascii char-set:printing))
(define %char-set:iso-control
(char-set-intersection char-set:ascii char-set:iso-control))
(define (string-start-arg s o)
(if (pair? o) (string-index->cursor s (car o)) 0))
(define (string-end-arg s o)
(if (pair? o) (string-index->cursor s (car o)) (string-length s)))
(define string-cursor? integer?)
(define string-cursor=? =)
(define string-cursor <)
(define string-cursor<=? <=)
(define string-cursor>? >)
(define string-cursor>=? >=)
(define string-cursor-ref string-ref)
(define (string-cursor-next s i) (+ i 1))
(define (string-cursor-prev s i) (- i 1))
(define substring-cursor substring)
(define (string-cursor->index str off) off)
(define (string-index->cursor str i) i)
(define (string-concatenate ls) (apply string-append ls))
(define (string-concatenate-reverse ls)
(string-concatenate (reverse ls)))
;; Replaced |\|| with \x7C;.
(include/resolve ("srfi" "%3a115") "regexp-impl.scm"))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a115/regexp/ 0000775 0000000 0000000 00000000000 14737542645 0020572 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a115/regexp/boundary-impl.scm 0000664 0000000 0000000 00000053345 14737542645 0024072 0 ustar 00root root 0000000 0000000 ;; Control
(define char-set:control (immutable-char-set (char-set-union (ucs-range->char-set 0 10) (ucs-range->char-set 11 13) (ucs-range->char-set 14 32) (ucs-range->char-set 127 160) (ucs-range->char-set 1536 1541) (ucs-range->char-set 8206 8208) (ucs-range->char-set 8234 8239) (ucs-range->char-set 8288 8293) (ucs-range->char-set 8294 8304) (ucs-range->char-set 55296 57344) (ucs-range->char-set 65520 65529) (ucs-range->char-set 65529 65532) (ucs-range->char-set 119155 119163) (ucs-range->char-set 917506 917536) (ucs-range->char-set 917536 917632) (ucs-range->char-set 917632 917760) (ucs-range->char-set 918000 921600))))
;; Extend,SpacingMark
(define char-set:extend-or-spacing-mark (immutable-char-set (char-set-union (char-set-union (ucs-range->char-set 768 880) (ucs-range->char-set 1155 1160) (ucs-range->char-set 1160 1162) (ucs-range->char-set 1425 1470) (ucs-range->char-set 1473 1475) (ucs-range->char-set 1476 1478) (ucs-range->char-set 1552 1563) (ucs-range->char-set 1611 1632) (ucs-range->char-set 1750 1757) (ucs-range->char-set 1759 1765) (ucs-range->char-set 1767 1769) (ucs-range->char-set 1770 1774) (ucs-range->char-set 1840 1867) (ucs-range->char-set 1958 1969) (ucs-range->char-set 2027 2036) (ucs-range->char-set 2070 2074) (ucs-range->char-set 2075 2084) (ucs-range->char-set 2085 2088) (ucs-range->char-set 2089 2094) (ucs-range->char-set 2137 2140) (ucs-range->char-set 2276 2303) (ucs-range->char-set 2304 2307) (ucs-range->char-set 2369 2377) (ucs-range->char-set 2385 2392) (ucs-range->char-set 2402 2404) (ucs-range->char-set 2497 2501) (ucs-range->char-set 2530 2532) (ucs-range->char-set 2561 2563) (ucs-range->char-set 2625 2627) (ucs-range->char-set 2631 2633) (ucs-range->char-set 2635 2638) (ucs-range->char-set 2672 2674) (ucs-range->char-set 2689 2691) (ucs-range->char-set 2753 2758) (ucs-range->char-set 2759 2761) (ucs-range->char-set 2786 2788) (ucs-range->char-set 2881 2885) (ucs-range->char-set 2914 2916) (ucs-range->char-set 3134 3137) (ucs-range->char-set 3142 3145) (ucs-range->char-set 3146 3150) (ucs-range->char-set 3157 3159) (ucs-range->char-set 3170 3172) (ucs-range->char-set 3276 3278) (ucs-range->char-set 3285 3287) (ucs-range->char-set 3298 3300) (ucs-range->char-set 3393 3397) (ucs-range->char-set 3426 3428) (ucs-range->char-set 3538 3541) (ucs-range->char-set 3636 3643) (ucs-range->char-set 3655 3663) (ucs-range->char-set 3764 3770) (ucs-range->char-set 3771 3773) (ucs-range->char-set 3784 3790) (ucs-range->char-set 3864 3866) (ucs-range->char-set 3953 3967) (ucs-range->char-set 3968 3973) (ucs-range->char-set 3974 3976) (ucs-range->char-set 3981 3992) (ucs-range->char-set 3993 4029) (ucs-range->char-set 4141 4145) (ucs-range->char-set 4146 4152) (ucs-range->char-set 4153 4155) (ucs-range->char-set 4157 4159) (ucs-range->char-set 4184 4186) (ucs-range->char-set 4190 4193) (ucs-range->char-set 4209 4213) (ucs-range->char-set 4229 4231) (ucs-range->char-set 4957 4960) (ucs-range->char-set 5906 5909) (ucs-range->char-set 5938 5941) (ucs-range->char-set 5970 5972) (ucs-range->char-set 6002 6004) (ucs-range->char-set 6068 6070) (ucs-range->char-set 6071 6078) (ucs-range->char-set 6089 6100) (ucs-range->char-set 6155 6158) (ucs-range->char-set 6432 6435) (ucs-range->char-set 6439 6441) (ucs-range->char-set 6457 6460) (ucs-range->char-set 6679 6681) (ucs-range->char-set 6744 6751) (ucs-range->char-set 6757 6765) (ucs-range->char-set 6771 6781) (ucs-range->char-set 6912 6916) (ucs-range->char-set 6966 6971) (ucs-range->char-set 7019 7028) (ucs-range->char-set 7040 7042) (ucs-range->char-set 7074 7078) (ucs-range->char-set 7080 7082) (ucs-range->char-set 7144 7146) (ucs-range->char-set 7151 7154) (ucs-range->char-set 7212 7220) (ucs-range->char-set 7222 7224) (ucs-range->char-set 7376 7379) (ucs-range->char-set 7380 7393) (ucs-range->char-set 7394 7401) (ucs-range->char-set 7616 7655) (ucs-range->char-set 7676 7680) (ucs-range->char-set 8204 8206) (ucs-range->char-set 8400 8413) (ucs-range->char-set 8413 8417) (ucs-range->char-set 8418 8421) (ucs-range->char-set 8421 8433) (ucs-range->char-set 11503 11506) (ucs-range->char-set 11744 11776) (ucs-range->char-set 12330 12334) (ucs-range->char-set 12334 12336) (ucs-range->char-set 12441 12443) (ucs-range->char-set 42608 42611) (ucs-range->char-set 42612 42622) (ucs-range->char-set 42736 42738) (ucs-range->char-set 43045 43047) (ucs-range->char-set 43232 43250) (ucs-range->char-set 43302 43310) (ucs-range->char-set 43335 43346) (ucs-range->char-set 43392 43395) (ucs-range->char-set 43446 43450) (ucs-range->char-set 43561 43567) (ucs-range->char-set 43569 43571) (ucs-range->char-set 43573 43575) (ucs-range->char-set 43698 43701) (ucs-range->char-set 43703 43705) (ucs-range->char-set 43710 43712) (ucs-range->char-set 43756 43758) (ucs-range->char-set 65024 65040) (ucs-range->char-set 65056 65063) (ucs-range->char-set 65438 65440) (ucs-range->char-set 68097 68100) (ucs-range->char-set 68101 68103) (ucs-range->char-set 68108 68112) (ucs-range->char-set 68152 68155) (ucs-range->char-set 69688 69703) (ucs-range->char-set 69760 69762) (ucs-range->char-set 69811 69815) (ucs-range->char-set 69817 69819) (ucs-range->char-set 69888 69891) (ucs-range->char-set 69927 69932) (ucs-range->char-set 69933 69941) (ucs-range->char-set 70016 70018) (ucs-range->char-set 70070 70079) (ucs-range->char-set 71344 71350) (ucs-range->char-set 94095 94099) (ucs-range->char-set 119143 119146) (ucs-range->char-set 119150 119155) (ucs-range->char-set 119163 119171) (ucs-range->char-set 119173 119180) (ucs-range->char-set 119210 119214) (ucs-range->char-set 119362 119365) (ucs-range->char-set 917760 918000)) (char-set-union (ucs-range->char-set 2366 2369) (ucs-range->char-set 2377 2381) (ucs-range->char-set 2382 2384) (ucs-range->char-set 2434 2436) (ucs-range->char-set 2495 2497) (ucs-range->char-set 2503 2505) (ucs-range->char-set 2507 2509) (ucs-range->char-set 2622 2625) (ucs-range->char-set 2750 2753) (ucs-range->char-set 2763 2765) (ucs-range->char-set 2818 2820) (ucs-range->char-set 2887 2889) (ucs-range->char-set 2891 2893) (ucs-range->char-set 3009 3011) (ucs-range->char-set 3014 3017) (ucs-range->char-set 3018 3021) (ucs-range->char-set 3073 3076) (ucs-range->char-set 3137 3141) (ucs-range->char-set 3202 3204) (ucs-range->char-set 3264 3266) (ucs-range->char-set 3267 3269) (ucs-range->char-set 3271 3273) (ucs-range->char-set 3274 3276) (ucs-range->char-set 3330 3332) (ucs-range->char-set 3391 3393) (ucs-range->char-set 3398 3401) (ucs-range->char-set 3402 3405) (ucs-range->char-set 3458 3460) (ucs-range->char-set 3536 3538) (ucs-range->char-set 3544 3551) (ucs-range->char-set 3570 3572) (ucs-range->char-set 3902 3904) (ucs-range->char-set 4155 4157) (ucs-range->char-set 4182 4184) (ucs-range->char-set 6078 6086) (ucs-range->char-set 6087 6089) (ucs-range->char-set 6435 6439) (ucs-range->char-set 6441 6444) (ucs-range->char-set 6448 6450) (ucs-range->char-set 6451 6457) (ucs-range->char-set 6581 6584) (ucs-range->char-set 6681 6683) (ucs-range->char-set 6765 6771) (ucs-range->char-set 6973 6978) (ucs-range->char-set 6979 6981) (ucs-range->char-set 7078 7080) (ucs-range->char-set 7084 7086) (ucs-range->char-set 7146 7149) (ucs-range->char-set 7154 7156) (ucs-range->char-set 7204 7212) (ucs-range->char-set 7220 7222) (ucs-range->char-set 7410 7412) (ucs-range->char-set 43043 43045) (ucs-range->char-set 43136 43138) (ucs-range->char-set 43188 43204) (ucs-range->char-set 43346 43348) (ucs-range->char-set 43444 43446) (ucs-range->char-set 43450 43452) (ucs-range->char-set 43453 43457) (ucs-range->char-set 43567 43569) (ucs-range->char-set 43571 43573) (ucs-range->char-set 43758 43760) (ucs-range->char-set 44003 44005) (ucs-range->char-set 44006 44008) (ucs-range->char-set 44009 44011) (ucs-range->char-set 69808 69811) (ucs-range->char-set 69815 69817) (ucs-range->char-set 70067 70070) (ucs-range->char-set 70079 70081) (ucs-range->char-set 71342 71344) (ucs-range->char-set 94033 94079)))))
;; Regional_Indicator
(define char-set:regional-indicator (immutable-char-set (char-set-union (ucs-range->char-set 127462 127488))))
;; :L
(define char-set:hangul-l (immutable-char-set (char-set-union (ucs-range->char-set 4352 4448) (ucs-range->char-set 43360 43389))))
;; :V
(define char-set:hangul-v (immutable-char-set (char-set-union (ucs-range->char-set 4448 4520) (ucs-range->char-set 55216 55239))))
;; :T
(define char-set:hangul-t (immutable-char-set (char-set-union (ucs-range->char-set 4520 4608) (ucs-range->char-set 55243 55292))))
;; :LV
(define char-set:hangul-lv (immutable-char-set (char-set-union)))
;; :LVT
(define char-set:hangul-lvt (immutable-char-set (char-set-union (ucs-range->char-set 44033 44060) (ucs-range->char-set 44061 44088) (ucs-range->char-set 44089 44116) (ucs-range->char-set 44117 44144) (ucs-range->char-set 44145 44172) (ucs-range->char-set 44173 44200) (ucs-range->char-set 44201 44228) (ucs-range->char-set 44229 44256) (ucs-range->char-set 44257 44284) (ucs-range->char-set 44285 44312) (ucs-range->char-set 44313 44340) (ucs-range->char-set 44341 44368) (ucs-range->char-set 44369 44396) (ucs-range->char-set 44397 44424) (ucs-range->char-set 44425 44452) (ucs-range->char-set 44453 44480) (ucs-range->char-set 44481 44508) (ucs-range->char-set 44509 44536) (ucs-range->char-set 44537 44564) (ucs-range->char-set 44565 44592) (ucs-range->char-set 44593 44620) (ucs-range->char-set 44621 44648) (ucs-range->char-set 44649 44676) (ucs-range->char-set 44677 44704) (ucs-range->char-set 44705 44732) (ucs-range->char-set 44733 44760) (ucs-range->char-set 44761 44788) (ucs-range->char-set 44789 44816) (ucs-range->char-set 44817 44844) (ucs-range->char-set 44845 44872) (ucs-range->char-set 44873 44900) (ucs-range->char-set 44901 44928) (ucs-range->char-set 44929 44956) (ucs-range->char-set 44957 44984) (ucs-range->char-set 44985 45012) (ucs-range->char-set 45013 45040) (ucs-range->char-set 45041 45068) (ucs-range->char-set 45069 45096) (ucs-range->char-set 45097 45124) (ucs-range->char-set 45125 45152) (ucs-range->char-set 45153 45180) (ucs-range->char-set 45181 45208) (ucs-range->char-set 45209 45236) (ucs-range->char-set 45237 45264) (ucs-range->char-set 45265 45292) (ucs-range->char-set 45293 45320) (ucs-range->char-set 45321 45348) (ucs-range->char-set 45349 45376) (ucs-range->char-set 45377 45404) (ucs-range->char-set 45405 45432) (ucs-range->char-set 45433 45460) (ucs-range->char-set 45461 45488) (ucs-range->char-set 45489 45516) (ucs-range->char-set 45517 45544) (ucs-range->char-set 45545 45572) (ucs-range->char-set 45573 45600) (ucs-range->char-set 45601 45628) (ucs-range->char-set 45629 45656) (ucs-range->char-set 45657 45684) (ucs-range->char-set 45685 45712) (ucs-range->char-set 45713 45740) (ucs-range->char-set 45741 45768) (ucs-range->char-set 45769 45796) (ucs-range->char-set 45797 45824) (ucs-range->char-set 45825 45852) (ucs-range->char-set 45853 45880) (ucs-range->char-set 45881 45908) (ucs-range->char-set 45909 45936) (ucs-range->char-set 45937 45964) (ucs-range->char-set 45965 45992) (ucs-range->char-set 45993 46020) (ucs-range->char-set 46021 46048) (ucs-range->char-set 46049 46076) (ucs-range->char-set 46077 46104) (ucs-range->char-set 46105 46132) (ucs-range->char-set 46133 46160) (ucs-range->char-set 46161 46188) (ucs-range->char-set 46189 46216) (ucs-range->char-set 46217 46244) (ucs-range->char-set 46245 46272) (ucs-range->char-set 46273 46300) (ucs-range->char-set 46301 46328) (ucs-range->char-set 46329 46356) (ucs-range->char-set 46357 46384) (ucs-range->char-set 46385 46412) (ucs-range->char-set 46413 46440) (ucs-range->char-set 46441 46468) (ucs-range->char-set 46469 46496) (ucs-range->char-set 46497 46524) (ucs-range->char-set 46525 46552) (ucs-range->char-set 46553 46580) (ucs-range->char-set 46581 46608) (ucs-range->char-set 46609 46636) (ucs-range->char-set 46637 46664) (ucs-range->char-set 46665 46692) (ucs-range->char-set 46693 46720) (ucs-range->char-set 46721 46748) (ucs-range->char-set 46749 46776) (ucs-range->char-set 46777 46804) (ucs-range->char-set 46805 46832) (ucs-range->char-set 46833 46860) (ucs-range->char-set 46861 46888) (ucs-range->char-set 46889 46916) (ucs-range->char-set 46917 46944) (ucs-range->char-set 46945 46972) (ucs-range->char-set 46973 47000) (ucs-range->char-set 47001 47028) (ucs-range->char-set 47029 47056) (ucs-range->char-set 47057 47084) (ucs-range->char-set 47085 47112) (ucs-range->char-set 47113 47140) (ucs-range->char-set 47141 47168) (ucs-range->char-set 47169 47196) (ucs-range->char-set 47197 47224) (ucs-range->char-set 47225 47252) (ucs-range->char-set 47253 47280) (ucs-range->char-set 47281 47308) (ucs-range->char-set 47309 47336) (ucs-range->char-set 47337 47364) (ucs-range->char-set 47365 47392) (ucs-range->char-set 47393 47420) (ucs-range->char-set 47421 47448) (ucs-range->char-set 47449 47476) (ucs-range->char-set 47477 47504) (ucs-range->char-set 47505 47532) (ucs-range->char-set 47533 47560) (ucs-range->char-set 47561 47588) (ucs-range->char-set 47589 47616) (ucs-range->char-set 47617 47644) (ucs-range->char-set 47645 47672) (ucs-range->char-set 47673 47700) (ucs-range->char-set 47701 47728) (ucs-range->char-set 47729 47756) (ucs-range->char-set 47757 47784) (ucs-range->char-set 47785 47812) (ucs-range->char-set 47813 47840) (ucs-range->char-set 47841 47868) (ucs-range->char-set 47869 47896) (ucs-range->char-set 47897 47924) (ucs-range->char-set 47925 47952) (ucs-range->char-set 47953 47980) (ucs-range->char-set 47981 48008) (ucs-range->char-set 48009 48036) (ucs-range->char-set 48037 48064) (ucs-range->char-set 48065 48092) (ucs-range->char-set 48093 48120) (ucs-range->char-set 48121 48148) (ucs-range->char-set 48149 48176) (ucs-range->char-set 48177 48204) (ucs-range->char-set 48205 48232) (ucs-range->char-set 48233 48260) (ucs-range->char-set 48261 48288) (ucs-range->char-set 48289 48316) (ucs-range->char-set 48317 48344) (ucs-range->char-set 48345 48372) (ucs-range->char-set 48373 48400) (ucs-range->char-set 48401 48428) (ucs-range->char-set 48429 48456) (ucs-range->char-set 48457 48484) (ucs-range->char-set 48485 48512) (ucs-range->char-set 48513 48540) (ucs-range->char-set 48541 48568) (ucs-range->char-set 48569 48596) (ucs-range->char-set 48597 48624) (ucs-range->char-set 48625 48652) (ucs-range->char-set 48653 48680) (ucs-range->char-set 48681 48708) (ucs-range->char-set 48709 48736) (ucs-range->char-set 48737 48764) (ucs-range->char-set 48765 48792) (ucs-range->char-set 48793 48820) (ucs-range->char-set 48821 48848) (ucs-range->char-set 48849 48876) (ucs-range->char-set 48877 48904) (ucs-range->char-set 48905 48932) (ucs-range->char-set 48933 48960) (ucs-range->char-set 48961 48988) (ucs-range->char-set 48989 49016) (ucs-range->char-set 49017 49044) (ucs-range->char-set 49045 49072) (ucs-range->char-set 49073 49100) (ucs-range->char-set 49101 49128) (ucs-range->char-set 49129 49156) (ucs-range->char-set 49157 49184) (ucs-range->char-set 49185 49212) (ucs-range->char-set 49213 49240) (ucs-range->char-set 49241 49268) (ucs-range->char-set 49269 49296) (ucs-range->char-set 49297 49324) (ucs-range->char-set 49325 49352) (ucs-range->char-set 49353 49380) (ucs-range->char-set 49381 49408) (ucs-range->char-set 49409 49436) (ucs-range->char-set 49437 49464) (ucs-range->char-set 49465 49492) (ucs-range->char-set 49493 49520) (ucs-range->char-set 49521 49548) (ucs-range->char-set 49549 49576) (ucs-range->char-set 49577 49604) (ucs-range->char-set 49605 49632) (ucs-range->char-set 49633 49660) (ucs-range->char-set 49661 49688) (ucs-range->char-set 49689 49716) (ucs-range->char-set 49717 49744) (ucs-range->char-set 49745 49772) (ucs-range->char-set 49773 49800) (ucs-range->char-set 49801 49828) (ucs-range->char-set 49829 49856) (ucs-range->char-set 49857 49884) (ucs-range->char-set 49885 49912) (ucs-range->char-set 49913 49940) (ucs-range->char-set 49941 49968) (ucs-range->char-set 49969 49996) (ucs-range->char-set 49997 50024) (ucs-range->char-set 50025 50052) (ucs-range->char-set 50053 50080) (ucs-range->char-set 50081 50108) (ucs-range->char-set 50109 50136) (ucs-range->char-set 50137 50164) (ucs-range->char-set 50165 50192) (ucs-range->char-set 50193 50220) (ucs-range->char-set 50221 50248) (ucs-range->char-set 50249 50276) (ucs-range->char-set 50277 50304) (ucs-range->char-set 50305 50332) (ucs-range->char-set 50333 50360) (ucs-range->char-set 50361 50388) (ucs-range->char-set 50389 50416) (ucs-range->char-set 50417 50444) (ucs-range->char-set 50445 50472) (ucs-range->char-set 50473 50500) (ucs-range->char-set 50501 50528) (ucs-range->char-set 50529 50556) (ucs-range->char-set 50557 50584) (ucs-range->char-set 50585 50612) (ucs-range->char-set 50613 50640) (ucs-range->char-set 50641 50668) (ucs-range->char-set 50669 50696) (ucs-range->char-set 50697 50724) (ucs-range->char-set 50725 50752) (ucs-range->char-set 50753 50780) (ucs-range->char-set 50781 50808) (ucs-range->char-set 50809 50836) (ucs-range->char-set 50837 50864) (ucs-range->char-set 50865 50892) (ucs-range->char-set 50893 50920) (ucs-range->char-set 50921 50948) (ucs-range->char-set 50949 50976) (ucs-range->char-set 50977 51004) (ucs-range->char-set 51005 51032) (ucs-range->char-set 51033 51060) (ucs-range->char-set 51061 51088) (ucs-range->char-set 51089 51116) (ucs-range->char-set 51117 51144) (ucs-range->char-set 51145 51172) (ucs-range->char-set 51173 51200) (ucs-range->char-set 51201 51228) (ucs-range->char-set 51229 51256) (ucs-range->char-set 51257 51284) (ucs-range->char-set 51285 51312) (ucs-range->char-set 51313 51340) (ucs-range->char-set 51341 51368) (ucs-range->char-set 51369 51396) (ucs-range->char-set 51397 51424) (ucs-range->char-set 51425 51452) (ucs-range->char-set 51453 51480) (ucs-range->char-set 51481 51508) (ucs-range->char-set 51509 51536) (ucs-range->char-set 51537 51564) (ucs-range->char-set 51565 51592) (ucs-range->char-set 51593 51620) (ucs-range->char-set 51621 51648) (ucs-range->char-set 51649 51676) (ucs-range->char-set 51677 51704) (ucs-range->char-set 51705 51732) (ucs-range->char-set 51733 51760) (ucs-range->char-set 51761 51788) (ucs-range->char-set 51789 51816) (ucs-range->char-set 51817 51844) (ucs-range->char-set 51845 51872) (ucs-range->char-set 51873 51900) (ucs-range->char-set 51901 51928) (ucs-range->char-set 51929 51956) (ucs-range->char-set 51957 51984) (ucs-range->char-set 51985 52012) (ucs-range->char-set 52013 52040) (ucs-range->char-set 52041 52068) (ucs-range->char-set 52069 52096) (ucs-range->char-set 52097 52124) (ucs-range->char-set 52125 52152) (ucs-range->char-set 52153 52180) (ucs-range->char-set 52181 52208) (ucs-range->char-set 52209 52236) (ucs-range->char-set 52237 52264) (ucs-range->char-set 52265 52292) (ucs-range->char-set 52293 52320) (ucs-range->char-set 52321 52348) (ucs-range->char-set 52349 52376) (ucs-range->char-set 52377 52404) (ucs-range->char-set 52405 52432) (ucs-range->char-set 52433 52460) (ucs-range->char-set 52461 52488) (ucs-range->char-set 52489 52516) (ucs-range->char-set 52517 52544) (ucs-range->char-set 52545 52572) (ucs-range->char-set 52573 52600) (ucs-range->char-set 52601 52628) (ucs-range->char-set 52629 52656) (ucs-range->char-set 52657 52684) (ucs-range->char-set 52685 52712) (ucs-range->char-set 52713 52740) (ucs-range->char-set 52741 52768) (ucs-range->char-set 52769 52796) (ucs-range->char-set 52797 52824) (ucs-range->char-set 52825 52852) (ucs-range->char-set 52853 52880) (ucs-range->char-set 52881 52908) (ucs-range->char-set 52909 52936) (ucs-range->char-set 52937 52964) (ucs-range->char-set 52965 52992) (ucs-range->char-set 52993 53020) (ucs-range->char-set 53021 53048) (ucs-range->char-set 53049 53076) (ucs-range->char-set 53077 53104) (ucs-range->char-set 53105 53132) (ucs-range->char-set 53133 53160) (ucs-range->char-set 53161 53188) (ucs-range->char-set 53189 53216) (ucs-range->char-set 53217 53244) (ucs-range->char-set 53245 53272) (ucs-range->char-set 53273 53300) (ucs-range->char-set 53301 53328) (ucs-range->char-set 53329 53356) (ucs-range->char-set 53357 53384) (ucs-range->char-set 53385 53412) (ucs-range->char-set 53413 53440) (ucs-range->char-set 53441 53468) (ucs-range->char-set 53469 53496) (ucs-range->char-set 53497 53524) (ucs-range->char-set 53525 53552) (ucs-range->char-set 53553 53580) (ucs-range->char-set 53581 53608) (ucs-range->char-set 53609 53636) (ucs-range->char-set 53637 53664) (ucs-range->char-set 53665 53692) (ucs-range->char-set 53693 53720) (ucs-range->char-set 53721 53748) (ucs-range->char-set 53749 53776) (ucs-range->char-set 53777 53804) (ucs-range->char-set 53805 53832) (ucs-range->char-set 53833 53860) (ucs-range->char-set 53861 53888) (ucs-range->char-set 53889 53916) (ucs-range->char-set 53917 53944) (ucs-range->char-set 53945 53972) (ucs-range->char-set 53973 54000) (ucs-range->char-set 54001 54028) (ucs-range->char-set 54029 54056) (ucs-range->char-set 54057 54084) (ucs-range->char-set 54085 54112) (ucs-range->char-set 54113 54140) (ucs-range->char-set 54141 54168) (ucs-range->char-set 54169 54196) (ucs-range->char-set 54197 54224) (ucs-range->char-set 54225 54252) (ucs-range->char-set 54253 54280) (ucs-range->char-set 54281 54308) (ucs-range->char-set 54309 54336) (ucs-range->char-set 54337 54364) (ucs-range->char-set 54365 54392) (ucs-range->char-set 54393 54420) (ucs-range->char-set 54421 54448) (ucs-range->char-set 54449 54476) (ucs-range->char-set 54477 54504) (ucs-range->char-set 54505 54532) (ucs-range->char-set 54533 54560) (ucs-range->char-set 54561 54588) (ucs-range->char-set 54589 54616) (ucs-range->char-set 54617 54644) (ucs-range->char-set 54645 54672) (ucs-range->char-set 54673 54700) (ucs-range->char-set 54701 54728) (ucs-range->char-set 54729 54756) (ucs-range->char-set 54757 54784) (ucs-range->char-set 54785 54812) (ucs-range->char-set 54813 54840) (ucs-range->char-set 54841 54868) (ucs-range->char-set 54869 54896) (ucs-range->char-set 54897 54924) (ucs-range->char-set 54925 54952) (ucs-range->char-set 54953 54980) (ucs-range->char-set 54981 55008) (ucs-range->char-set 55009 55036) (ucs-range->char-set 55037 55064) (ucs-range->char-set 55065 55092) (ucs-range->char-set 55093 55120) (ucs-range->char-set 55121 55148) (ucs-range->char-set 55149 55176) (ucs-range->char-set 55177 55204))))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a115/regexp/boundary.sls 0000664 0000000 0000000 00000002004 14737542645 0023134 0 ustar 00root root 0000000 0000000 ;; Character sets for Unicode boundaries, TR29.
;; This code is written by Alex Shinn and placed in the
;; Public Domain. All warranties are disclaimed.
;;> Char-sets used for
;;> \hyperlink["http://unicode.org/reports/tr29/"]{TR29} word
;;> boundaries.
;; Converted from lib/chibi/char-set/boundary.sld to R6RS by Göran Weinholt.
(library (srfi :115 regexp boundary)
(export char-set:regional-indicator
char-set:extend-or-spacing-mark
char-set:hangul-l
char-set:hangul-v
char-set:hangul-t
char-set:hangul-lv
char-set:hangul-lvt)
(import (rnrs)
(srfi :14 char-sets)
(srfi private include))
(define (immutable-char-set cs) cs)
;; generated with:
;; tools/extract-unicode-props.scm --derived GraphemeBreakProperty.txt
;; Control extend-or-spacing-mark=Extend,SpacingMark Regional_Indicator
;; hangul-l=:L hangul-v=:V hangul-t=:T hangul-lv=:LV hangul-lvt=:LVT
(include/resolve ("srfi" "%3a115" "regexp") "boundary-impl.scm"))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a116.sls 0000664 0000000 0000000 00000002632 14737542645 0020027 0 ustar 00root root 0000000 0000000 (library (srfi :116)
(export iq
ipair ilist xipair ipair* make-ilist ilist-copy ilist-tabulate iiota
ipair?
proper-ilist? ilist? dotted-ilist? not-ipair? null-ilist? ilist=
icar icdr ilist-ref
ifirst isecond ithird ifourth ififth isixth iseventh ieighth ininth itenth
icaar icadr icdar icddr
icaaar icaadr icadar icaddr icdaar icdadr icddar icdddr
icaaaar icaaadr icaadar icaaddr icadaar icadadr icaddar icadddr
icdaaar icdaadr icdadar icdaddr icddaar icddadr icdddar icddddr
icar+icdr itake idrop ilist-tail
itake-right idrop-right isplit-at ilast last-ipair
ilength iappend iconcatenate ireverse iappend-reverse
izip iunzip1 iunzip2 iunzip3 iunzip4 iunzip5
icount imap ifor-each ifold iunfold ipair-fold ireduce
ifold-right iunfold-right ipair-fold-right ireduce-right
iappend-map ipair-for-each ifilter-map imap-in-order
ifilter ipartition iremove imember imemq imemv
ifind ifind-tail iany ievery
ilist-index itake-while idrop-while ispan ibreak
idelete idelete-duplicates
iassoc iassq iassv ialist-cons ialist-delete
replace-icar replace-icdr
pair->ipair ipair->pair list->ilist ilist->list
tree->itree itree->tree gtree->itree gtree->tree
iapply)
(import (srfi :116 ilists))) chez-srfi-0.0+git20241031.b424440+dfsg/%3a116/ 0000775 0000000 0000000 00000000000 14737542645 0017301 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a116/ilists.sls 0000664 0000000 0000000 00000126276 14737542645 0021351 0 ustar 00root root 0000000 0000000 (library (srfi :116 ilists)
(export iq
ipair ilist xipair ipair* make-ilist ilist-copy ilist-tabulate iiota
ipair?
proper-ilist? ilist? dotted-ilist? not-ipair? null-ilist? ilist=
icar icdr ilist-ref
ifirst isecond ithird ifourth ififth isixth iseventh ieighth ininth itenth
icaar icadr icdar icddr
icaaar icaadr icadar icaddr icdaar icdadr icddar icdddr
icaaaar icaaadr icaadar icaaddr icadaar icadadr icaddar icadddr
icdaaar icdaadr icdadar icdaddr icddaar icddadr icdddar icddddr
icar+icdr itake idrop ilist-tail
itake-right idrop-right isplit-at ilast last-ipair
ilength iappend iconcatenate ireverse iappend-reverse
izip iunzip1 iunzip2 iunzip3 iunzip4 iunzip5
icount imap ifor-each ifold iunfold ipair-fold ireduce
ifold-right iunfold-right ipair-fold-right ireduce-right
iappend-map ipair-for-each ifilter-map imap-in-order
ifilter ipartition iremove imember imemq imemv
ifind ifind-tail iany ievery
ilist-index itake-while idrop-while ispan ibreak
idelete idelete-duplicates
iassoc iassq iassv ialist-cons ialist-delete
replace-icar replace-icdr
pair->ipair ipair->pair list->ilist ilist->list
tree->itree itree->tree gtree->itree gtree->tree
iapply)
(import (except (rnrs) define-record-type)
(only (srfi :1) set-cdr! set-car!)
(srfi :9))
(define (error* msg . args)
(error 'ilist msg args))
;;;; Enhancements and hooks in Olin's SRFI-1 code to make it work for ilists
;;; The basic ilist cell
(define-record-type (ipair icar icdr) ipair? (icar icar) (icdr icdr))
;;; SRFI 8 syntax for receiving multiple values
(define-syntax receive
(syntax-rules ()
((receive formals expression body ...)
(call-with-values (lambda () expression)
(lambda formals body ...)))))
;;; Syntax for quoting ilists
(define-syntax iq
(syntax-rules ()
((iq . tree) (gtree->itree 'tree))))
;;; Replacers
(define (replace-icar old new)
(ipair new (icdr old)))
(define (replace-icdr old new)
(ipair (icar old) new))
;;; Conversion between lists and ilists
(define (pair->ipair pair)
(ipair (car pair) (cdr pair)))
(define (ipair->pair ipair)
(cons (icar ipair) (icdr ipair)))
(define (list->ilist list)
(let lp ((list list))
(if (pair? list)
(ipair (car list) (lp (cdr list)))
list)))
(define (ilist . objs)
(list->ilist objs))
(define (ilist->list ilist)
(let lp ((ilist ilist))
(if (ipair? ilist)
(cons (icar ilist) (lp (icdr ilist)))
ilist)))
(define (tree->itree obj)
(if (pair? obj)
(ipair (tree->itree (car obj)) (tree->itree (cdr obj)))
obj))
(define (itree->tree obj)
(if (ipair? obj)
(cons (itree->tree (icar obj)) (itree->tree (icdr obj)))
obj))
(define (gtree->itree obj)
(cond
((pair? obj)
(ipair (gtree->itree (car obj)) (gtree->itree (cdr obj))))
((ipair? obj)
(ipair (gtree->itree (icar obj)) (gtree->itree (icdr obj))))
(else
obj)))
(define (gtree->tree obj)
(cond
((pair? obj)
(cons (gtree->tree (car obj)) (gtree->tree (cdr obj))))
((ipair? obj)
(cons (gtree->tree (icar obj)) (gtree->tree (icdr obj))))
(else
obj)))
;; Apply a function to (arguments and) an ilist
;; If ilists are built in, optimize this!
;; Need a few SRFI-1 routines
(define (take! ls i)
(if (<= i 0)
'()
(let ((tail (list-tail ls (- i 1))))
(set-cdr! tail '())
ls)))
(define (drop-right! ls i)
(take! ls (- (length ls) i)))
(define (last ls) (if (null? (cdr ls)) (car ls) (last (cdr ls))))
(define (iapply proc . ilists)
(cond
((null? ilists)
(apply proc '()))
((null? (cdr ilists))
(apply proc (ilist->list (car ilists))))
(else
(let ((final (ilist->list (last ilists))))
(apply proc (append (drop-right! ilists 1) final))))))
;;; Printer for debugging
(define (write-ipair ipair port)
(write (gtree->tree ipair) port))
;;; Stuff needed by Olin's code
(define-syntax :optional
(syntax-rules ()
((:optional rest default)
(cond
((null? rest) default)
((null? (cdr rest)) (car rest))
(else (error* "Too many arguments"))))))
;;; Analogues of R5RS list routines (not defined by Olin)
(define (iassq x lis)
(ifind (lambda (entry) (eq? x (icar entry))) lis))
(define (iassv x lis)
(ifind (lambda (entry) (eqv? x (icar entry))) lis))
(define (ifor-each proc lis1 . lists)
(check-arg procedure? proc ipair-for-each)
(if (pair? lists)
(let lp ((lists (cons lis1 lists)))
(let ((tails (%cdrs lists)))
(if (pair? tails)
(begin (apply proc (map icar lists))
(lp tails)))))
;; Fast path.
(let lp ((lis lis1))
(if (not (null-ilist? lis))
(let ((tail (icdr lis))) ; Grab the icdr now,
(proc (icar lis)) ; even though it's unnecessary
(lp tail))))))
;;; SRFI-116 ilist-processing library -*- Scheme -*-
;;; Sample implementation
;;;
;;; Copyright (c) 1998, 1999 by Olin Shivers.
;;; Modifications Copyright (c) 2014 by John Cowan.
;;; You may do as you please with
;;; this code as long as you do not remove either copyright notice
;;; or hold us liable for its use. Please send bug reports to
;;; .
;;; This is a library of ilist- and ipair-processing functions. I wrote it after
;;; carefully considering the functions provided by the libraries found in
;;; R4RS/R5RS Scheme, MIT Scheme, Gambit, RScheme, MzScheme, slib, Common
;;; Lisp, Bigloo, guile, T, APL and the SML standard basis. It is a pretty
;;; rich toolkit, providing a superset of the functionality found in any of
;;; the various Schemes I considered.
;;; This implementation is intended as a portable reference implementation
;;; for SRFI-116. See the porting notes below for more information.
;;; Exported:
;;; xipair tree-copy make-ilist ilist-tabulate ipair* ilist-copy
;;; proper-ilist? circular-ilist? dotted-ilist? not-ipair? null-ilist? ilist=
;;; ilength+
;;; iiota
;;; ifirst isecond ithird ifourth ififth isixth iseventh ieighth ininth itenth
;;; icar+icdr
;;; itake idrop
;;; itake-right idrop-right
;;; isplit-at
;;; ilast last-ipair
;;; izip iunzip1 iunzip2 iunzip3 iunzip4 iunzip5
;;; icount
;;; iappend-reverse iconcatenate
;;; iunfold ifold ipair-fold ireduce
;;; iunfold-right ifold-right ipair-fold-right ireduce-right
;;; iappend-map ipair-for-each ifilter-map imap-in-order
;;; ifilter ipartition iremove
;;; ifind ifind-tail iany ievery ilist-index
;;; itake-while idrop-while
;;; ispan ibreak
;;; idelete
;;; ialist-cons alist-copy
;;; idelete-duplicates
;;; ialist-delete
;;; ipair ipair? null? icar icdr
;;; ilist ilength iappend ireverse icadr ... icddddr ilist-ref
;;; first second third fourth fifth sixth seventh eighth ninth tenth
;;; imemq imemv iassq iassv
;;;
;;; ilist-tail (same as idrop)
;;; ilist? (same as proper-ilist?)
;;; A note on recursion and iteration/reversal:
;;; Many iterative ilist-processing algorithms naturally compute the elements
;;; of the answer ilist in the wrong order (left-to-right or head-to-tail) from
;;; the order needed to pair them into the proper answer (right-to-left, or
;;; tail-then-head). One style or idiom of programming these algorithms, then,
;;; loops, consing up the elements in reverse order, then destructively
;;; reverses the ilist at the end of the loop. I do not do this. The natural
;;; and efficient way to code these algorithms is recursively. This trades off
;;; intermediate temporary ilist structure for intermediate temporary stack
;;; structure. In a stack-based system, this improves cache locality and
;;; lightens the load on the GC system. Don't stand on your head to iterate!
;;; Recurse, where natural. Multiple-value returns make this even more
;;; convenient, when the recursion/iteration has multiple state values.
;;; Porting:
;;; This is carefully tuned code; do not modify casually.
;;; - It is careful to share storage when possible;
;;;
;;; That said, a port of this library to a specific Scheme system might wish
;;; to tune this code to exploit particulars of the implementation.
;;; The single most important compiler-specific optimisation you could make
;;; to this library would be to add rewrite rules or transforms to:
;;; - transform applications of n-ary procedures (e.g. ILIST=, IPAIR*, IIAPPEND,
;;; ILSET-UNION) into multiple applications of a primitive two-argument
;;; variant.
;;; - transform applications of the mapping functions (IMAP, IFOR-EACH, IFOLD,
;;; IANY, IEVERY) into open-coded loops. The killer here is that these
;;; functions are n-ary. Handling the general case is quite inefficient,
;;; requiring many intermediate data structures to be allocated and
;;; discarded.
;;; - transform applications of procedures that take optional arguments
;;; into calls to variants that do not take optional arguments. This
;;; eliminates unnecessary consing and parsing of the rest parameter.
;;;
;;; These transforms would provide BIG speedups. In particular, the n-ary
;;; mapping functions are particularly slow and pair-intensive, and are good
;;; candidates for tuning. I have coded fast paths for the single-ilist cases,
;;; but what you really want to do is exploit the fact that the compiler
;;; usually knows how many arguments are being passed to a particular
;;; application of these functions -- they are usually explicitly called, not
;;; passed around as higher-order values. If you can arrange to have your
;;; compiler produce custom code or custom linkages based on the number of
;;; arguments in the call, you can speed these functions up a *lot*. But this
;;; kind of compiler technology no longer exists in the Scheme world as far as
;;; I can see.
;;;
;;; This code does a fair amount of run-time argument checking. If your
;;; Scheme system has a sophisticated compiler that can eliminate redundant
;;; error checks, this is no problem. However, if not, these checks incur
;;; some performance overhead -- and, in a safe Scheme implementation, they
;;; are in some sense redundant: if we don't check to see that the PROC
;;; parameter is a procedure, we'll find out anyway three lines later when
;;; we try to call the value. It's pretty easy to rip all this argument
;;; checking code out if it's inappropriate for your implementation -- just
;;; nuke every call to CHECK-ARG.
;;;
;;; On the other hand, if you *do* have a sophisticated compiler that will
;;; actually perform soft-typing and eliminate redundant checks (Rice's systems
;;; being the only possible candidate of which I'm aware), leaving these checks
;;; in can *help*, since their presence can be elided in redundant cases,
;;; and in cases where they are needed, performing the checks early, at
;;; procedure entry, can "lift" a check out of a loop.
;;;
;;; Finally, I have only checked the properties that can portably be checked
;;; with R5RS Scheme -- and this is not complete. You may wish to alter
;;; the CHECK-ARG parameter checks to perform extra, implementation-specific
;;; checks, such as procedure arity for higher-order values.
;;;
;;; The code has only these non-R4RS dependencies:
;;; A few calls to an ERROR procedure;
;;; Uses of the R5RS multiple-value procedure VALUES and the m-v binding
;;; RECEIVE macro (which isn't R5RS, but is a trivial macro).
;;; Many calls to a parameter-checking procedure check-arg:
(define (check-arg pred val caller)
(let lp ((val val))
(if (pred val) val (lp (error* "Bad argument" val pred caller)))))
;;; A few uses of the LET-OPTIONAL and :OPTIONAL macros for parsing
;;; optional arguments.
;;;
;;; Most of these procedures use the NULL-ILIST? test to trigger the
;;; base case in the inner loop or recursion. The NULL-ILIST? function
;;; is defined to be a careful one -- it raises an error if passed a
;;; non-nil, non-ipair value. The spec allows an implementation to use
;;; a less-careful implementation that simply defines NULL-ILIST? to
;;; be NOT-IPAIR?. This would speed up the inner loops of these procedures
;;; at the expense of having them silently accept dotted lists.
;;; A note on dotted lists:
;;; Many of the procedures in this library can be trivially redefined
;;; to handle dotted lists, just by changing the NULL-ILIST? base-case
;;; check to NOT-IPAIR?, meaning that any non-ipair value is taken to be
;;; an empty ilist. For most of these procedures, that's all that is
;;; required.
;;;
;;; However, we have to do a little more work for some procedures that
;;; *produce* ilists from other ilists. Were we to extend these procedures to
;;; accept dotted ilists, we would have to define how they terminate the ilists
;;; produced as results when passed a dotted ilist.
;;;
;;; The argument *against* defining these procedures to work on dotted
;;; ilists is that dotted ilists are the rare, odd case, and that by
;;; arranging for the procedures to handle them, we lose error checking
;;; in the cases where a dotted ilist is passed by accident -- e.g., when
;;; the programmer swaps a two arguments to an ilist-processing function,
;;; one being a scalar and one being an ilist. For example,
;;; (imember '(1 3 5 7 9) 7)
;;; This would quietly return #f if we extended IMEMBER to accept dotted
;;; lists.
;;; Constructors
;;;;;;;;;;;;;;;;
;;; Occasionally useful as a value to be passed to a ifold or other
;;; higher-order procedure.
(define (xipair d a) (ipair a d))
;;;; Recursively copy every ipair.
;;; Make an ilist of length LEN.
(define (make-ilist len . maybe-elt)
(check-arg (lambda (n) (and (integer? n) (>= n 0))) len make-ilist)
(let ((elt (cond ((null? maybe-elt) #f) ; Default value
((null? (cdr maybe-elt)) (car maybe-elt))
(else (error* "Too many arguments to MAKE-ILIST"
(ipair len maybe-elt))))))
(do ((i len (- i 1))
(ans '() (ipair elt ans)))
((<= i 0) ans))))
;; The ilist procedure is defined in ilists-base.scm.
;;; Make an ilist of ilength LEN. Elt i is (PROC i) for 0 <= i < LEN.
(define (ilist-tabulate len proc)
(check-arg (lambda (n) (and (integer? n) (>= n 0))) len ilist-tabulate)
(check-arg procedure? proc ilist-tabulate)
(do ((i (- len 1) (- i 1))
(ans '() (ipair (proc i) ans)))
((< i 0) ans)))
;;; (ipair* a1 a2 ... an) = (ipair a1 (ipair a2 (ipair ... an)))
;;; (ipair* a1) = a1 (ipair* a1 a2 ...) = (ipair a1 (ipair* a2 ...))
;;;
;;; (ipair ifirst (iunfold not-ipair? icar icdr rest values))
(define (ipair* ifirst . rest)
(let recur ((x ifirst) (rest rest))
(if (pair? rest)
(ipair x (recur (car rest) (cdr rest)))
x)))
;;; (iunfold not-ipair? icar icdr lis values)
(define (ilist-copy lis)
(let recur ((lis lis))
(if (ipair? lis)
(ipair (icar lis) (recur (icdr lis)))
lis)))
;;; IIOTA count [start step] (start start+step ... start+(count-1)*step)
(define (iiota count . maybe-start+step)
(check-arg integer? count iiota)
(if (< count 0) (error* "Negative step count" iiota count))
(let ((start (if (pair? maybe-start+step) (car maybe-start+step) 0))
(step (if (and (pair? maybe-start+step)
(pair? (cdr maybe-start+step)))
(cadr maybe-start+step)
1)))
(check-arg number? start iiota)
(check-arg number? step iiota)
(let loop ((n 0) (r '()))
(if (= n count)
(ireverse r)
(loop (+ 1 n)
(ipair (+ start (* n step)) r))))))
;;; ::= () ; Empty proper ilist
;;; | (ipair ) ; Proper-ilist ipair
;;; Note that this definition rules out circular lists -- and this
;;; function is required to detect this case and return false.
(define (ilist? x) (proper-ilist? x))
(define (proper-ilist? x)
(let lp ((x x) (lag x))
(if (ipair? x)
(let ((x (icdr x)))
(if (ipair? x)
(let ((x (icdr x))
(lag (icdr lag)))
(and (not (eq? x lag)) (lp x lag)))
(null? x)))
(null? x))))
;;; A dotted ilist is a finite ilist (possibly of ilength 0) terminated
;;; by a non-nil value. Any non-ipair, non-nil value (e.g., "foo" or 5)
;;; is a dotted ilist of ilength 0.
;;;
;;; ::= ; Empty dotted ilist
;;; | (ipair ) ; Proper-ilist ipair
(define (dotted-ilist? x)
(let lp ((x x) (lag x))
(if (ipair? x)
(let ((x (icdr x)))
(if (ipair? x)
(let ((x (icdr x))
(lag (icdr lag)))
(and (not (eq? x lag)) (lp x lag)))
(not (null? x))))
(not (null? x)))))
(define (not-ipair? x) (not (ipair? x))) ; Inline me.
;;; This is a legal definition which is fast and sloppy:
;;; (define null-ilist? not-ipair?)
;;; but we'll provide a more careful one:
(define (null-ilist? l)
(cond ((ipair? l) #f)
((null? l) #t)
(else (error* "null-ilist?: argument out of domain" l))))
(define (ilist= = . ilists)
(or (null? ilists) ; special case
(let lp1 ((ilist-a (car ilists)) (others (cdr ilists)))
(or (null? others)
(let ((ilist-b (car others))
(others (cdr others)))
(if (eq? ilist-a ilist-b) ; EQ? => LIST=
(lp1 ilist-b others)
(let lp2 ((pair-a ilist-a) (pair-b ilist-b))
(if (null-ilist? pair-a)
(and (null-ilist? pair-b)
(lp1 ilist-b others))
(and (not (null-ilist? pair-b))
(= (icar pair-a) (icar pair-b))
(lp2 (icdr pair-a) (icdr pair-b)))))))))))
(define (ilength x) ; ILENGTH may diverge or
(let lp ((x x) (len 0)) ; raise an error if X is
(if (ipair? x) ; a circular ilist. This version
(lp (icdr x) (+ len 1)) ; diverges.
len)))
(define (izip ilist1 . more-lists) (apply imap ilist ilist1 more-lists))
;;; Selectors
;;;;;;;;;;;;;
(define (icaar x) (icar (icar x)))
(define (icadr x) (icar (icdr x)))
(define (icdar x) (icdr (icar x)))
(define (icddr x) (icdr (icdr x)))
(define (icaaar x) (icaar (icar x)))
(define (icaadr x) (icaar (icdr x)))
(define (icadar x) (icadr (icar x)))
(define (icaddr x) (icadr (icdr x)))
(define (icdaar x) (icdar (icar x)))
(define (icdadr x) (icdar (icdr x)))
(define (icddar x) (icddr (icar x)))
(define (icdddr x) (icddr (icdr x)))
(define (icaaaar x) (icaaar (icar x)))
(define (icaaadr x) (icaaar (icdr x)))
(define (icaadar x) (icaadr (icar x)))
(define (icaaddr x) (icaadr (icdr x)))
(define (icadaar x) (icadar (icar x)))
(define (icadadr x) (icadar (icdr x)))
(define (icaddar x) (icaddr (icar x)))
(define (icadddr x) (icaddr (icdr x)))
(define (icdaaar x) (icdaar (icar x)))
(define (icdaadr x) (icdaar (icdr x)))
(define (icdadar x) (icdadr (icar x)))
(define (icdaddr x) (icdadr (icdr x)))
(define (icddaar x) (icddar (icar x)))
(define (icddadr x) (icddar (icdr x)))
(define (icdddar x) (icdddr (icar x)))
(define (icddddr x) (icdddr (icdr x)))
(define ifirst icar)
(define isecond icadr)
(define ithird icaddr)
(define ifourth icadddr)
(define (ififth x) (icar (icddddr x)))
(define (isixth x) (icadr (icddddr x)))
(define (iseventh x) (icaddr (icddddr x)))
(define (ieighth x) (icadddr (icddddr x)))
(define (ininth x) (icar (icddddr (icddddr x))))
(define (itenth x) (icadr (icddddr (icddddr x))))
(define (icar+icdr ipair) (values (icar ipair) (icdr ipair)))
;;; itake & idrop
(define (itake lis k)
(check-arg integer? k itake)
(let recur ((lis lis) (k k))
(if (zero? k) '()
(ipair (icar lis)
(recur (icdr lis) (- k 1))))))
(define (ilist-tail lis k) (idrop lis k))
(define (idrop lis k)
(check-arg integer? k idrop)
(let iter ((lis lis) (k k))
(if (zero? k) lis (iter (icdr lis) (- k 1)))))
;;; ITAKE-RIGHT and IDROP-RIGHT work by getting two pointers into the ilist,
;;; off by K, then chasing down the ilist until the lead pointer falls off
;;; the end.
(define (itake-right lis k)
(check-arg integer? k itake-right)
(let lp ((lag lis) (lead (idrop lis k)))
(if (ipair? lead)
(lp (icdr lag) (icdr lead))
lag)))
(define (idrop-right lis k)
(check-arg integer? k idrop-right)
(let recur ((lag lis) (lead (idrop lis k)))
(if (ipair? lead)
(ipair (icar lag) (recur (icdr lag) (icdr lead)))
'())))
;;; In this function, LEAD is actually K+1 ahead of LAG. This lets
;;; us stop LAG one step early, in time to smash its icdr to ().
(define (ilist-ref lis i) (icar (idrop lis i))) ; R4RS
(define (isplit-at x k)
(check-arg integer? k isplit-at)
(let recur ((lis x) (k k))
(if (zero? k) (values '() lis)
(receive (prefix suffix) (recur (icdr lis) (- k 1))
(values (ipair (icar lis) prefix) suffix)))))
(define (ilast lis) (icar (last-ipair lis)))
(define (last-ipair lis)
(check-arg ipair? lis last-ipair)
(let lp ((lis lis))
(let ((tail (icdr lis)))
(if (ipair? tail) (lp tail) lis))))
;;; Unzippers -- 1 through 5
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (iunzip1 lis) (imap icar lis))
(define (iunzip2 lis)
(let recur ((lis lis))
(if (null-ilist? lis) (values lis lis) ; Use NOT-IPAIR? to handle
(let ((elt (icar lis))) ; dotted lists.
(receive (a b) (recur (icdr lis))
(values (ipair (icar elt) a)
(ipair (icadr elt) b)))))))
(define (iunzip3 lis)
(let recur ((lis lis))
(if (null-ilist? lis) (values lis lis lis)
(let ((elt (icar lis)))
(receive (a b c) (recur (icdr lis))
(values (ipair (icar elt) a)
(ipair (icadr elt) b)
(ipair (icaddr elt) c)))))))
(define (iunzip4 lis)
(let recur ((lis lis))
(if (null-ilist? lis) (values lis lis lis lis)
(let ((elt (icar lis)))
(receive (a b c d) (recur (icdr lis))
(values (ipair (icar elt) a)
(ipair (icadr elt) b)
(ipair (icaddr elt) c)
(ipair (icadddr elt) d)))))))
(define (iunzip5 lis)
(let recur ((lis lis))
(if (null-ilist? lis) (values lis lis lis lis lis)
(let ((elt (icar lis)))
(receive (a b c d e) (recur (icdr lis))
(values (ipair (icar elt) a)
(ipair (icadr elt) b)
(ipair (icaddr elt) c)
(ipair (icadddr elt) d)
(ipair (icar (icddddr elt)) e)))))))
;;; iappend-reverse iconcatenate
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (iappend . lists)
(if (pair? lists)
(let recur ((list1 (car lists)) (lists (cdr lists)))
(if (pair? lists)
(let ((tail (recur (car lists) (cdr lists))))
(ifold-right ipair tail list1)) ; Append LIST1 & TAIL.
list1))
'()))
;; (define (iappend-reverse rev-head tail) (ifold ipair tail rev-head))
;;; Hand-inline the IFOLD and PAIR-IFOLD ops for speed.
(define (iappend-reverse rev-head tail)
(let lp ((rev-head rev-head) (tail tail))
(if (null-ilist? rev-head) tail
(lp (icdr rev-head) (ipair (icar rev-head) tail)))))
(define (iconcatenate lists) (ireduce-right iappend '() lists))
;;; Fold/imap internal utilities
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; These little internal utilities are used by the general
;;; ifold & mapper funs for the n-ary cases . It'd be nice if they got inlined.
;;; One the other hand, the n-ary cases are painfully inefficient as it is.
;;; An aggressive implementation should simply re-write these functions
;;; for raw efficiency; I have written them for as much clarity, portability,
;;; and simplicity as can be achieved.
;;;
;;; I use the dreaded call/cc to do local aborts. A good compiler could
;;; handle this with extreme efficiency. An implementation that provides
;;; a one-shot, non-persistent continuation grabber could help the compiler
;;; out by using that in place of the call/cc's in these routines.
;;;
;;; These functions have funky definitions that are precisely tuned to
;;; the needs of the ifold/imap procs -- for example, to minimize the number
;;; of times the argument lists need to be examined.
;;; Return (map icdr ilists).
;;; However, if any element of ILISTS is empty, just abort and return '().
(define (%cdrs lists)
(call-with-current-continuation
(lambda (abort)
(let recur ((lists lists))
(if (pair? lists)
(let ((lis (car lists)))
(if (null? lis) (abort '())
(cons (icdr lis) (recur (cdr lists)))))
'())))))
(define (%cars+ lists last-elt) ; (append (map icar lists) (list last-elt))
(let recur ((lists lists))
(if (pair? lists)
(cons (icar (car lists)) (recur (cdr lists)))
(list last-elt))))
;;; LISTS is a (not very long) non-empty list of ilists.
;;; Return two lists: the icars & the icdrs of the ilists.
;;; However, if any of the ilists is empty, just abort and return [() ()].
(define (%cars+cdrs ilists)
(call-with-current-continuation
(lambda (abort)
(let recur ((ilists ilists))
(if (pair? ilists)
(let ((ilist (car ilists))
(other-ilists (cdr ilists)))
(if (null? ilist) (abort '() '()) ; LIST is empty -- bail out
(let ((a (icar ilist))
(d (icdr ilist)))
(receive (icars icdrs) (recur other-ilists)
(values (cons a icars) (cons d icdrs))))))
(values '() '()))))))
;;; Like %CARS+CDRS, but we pass in a final elt tacked onto the end of the
;;; cars ilist. What a hack.
(define (%cars+cdrs+ ilists cars-final)
(call-with-current-continuation
(lambda (abort)
(let recur ((ilists ilists))
(if (pair? ilists)
(let ((ilist (car ilists))
(other-ilists (cdr ilists)))
(if (null? ilist) (abort '() '()) ; LIST is empty -- bail out
(receive (a d) (icar+icdr ilist)
(receive (cars cdrs) (recur other-ilists)
(values (cons a cars) (cons d cdrs))))))
(values (list cars-final) '()))))))
;;; Like %CARS+CDRS, but blow up if any ilist is empty.
(define (%cars+cdrs/no-test ilists)
(let recur ((ilists ilists))
(if (pair? ilists)
(let ((ilist (car ilists))
(other-ilists (cdr ilists)))
(let ((a (icar ilist))
(d (icdr ilist)))
(receive (cars cdrs) (recur other-ilists)
(values (cons a cars) (cons d cdrs)))))
(values '() '()))))
;;; icount
;;;;;;;;;
(define (icount pred ilist1 . ilists)
(check-arg procedure? pred icount)
(if (pair? ilists)
;; N-ary case
(let lp ((ilist1 ilist1) (ilists ilists) (i 0))
(if (null-ilist? ilist1) i
(receive (as ds) (%cars+cdrs ilists)
(if (null? as) i
(lp (icdr ilist1) ds
(if (apply pred (icar ilist1) as) (+ i 1) i))))))
;; Fast path
(let lp ((lis ilist1) (i 0))
(if (null-ilist? lis) i
(lp (icdr lis) (if (pred (icar lis)) (+ i 1) i))))))
;;; ifold/iunfold
;;;;;;;;;;;;;;;
(define (iunfold-right p f g seed . maybe-tail)
(check-arg procedure? p iunfold-right)
(check-arg procedure? f iunfold-right)
(check-arg procedure? g iunfold-right)
(let lp ((seed seed) (ans (:optional maybe-tail '())))
(if (p seed) ans
(lp (g seed)
(ipair (f seed) ans)))))
(define (iunfold p f g seed . maybe-tail-gen)
(check-arg procedure? p iunfold)
(check-arg procedure? f iunfold)
(check-arg procedure? g iunfold)
(if (pair? maybe-tail-gen)
(let ((tail-gen (car maybe-tail-gen)))
(if (pair? (cdr maybe-tail-gen))
(apply error* "Too many arguments" iunfold p f g seed maybe-tail-gen)
(let recur ((seed seed))
(if (p seed) (tail-gen seed)
(ipair (f seed) (recur (g seed)))))))
(let recur ((seed seed))
(if (p seed) '()
(ipair (f seed) (recur (g seed)))))))
(define (ifold kons knil ilis1 . ilists)
(check-arg procedure? kons ifold)
(if (pair? ilists)
(let lp ((ilists (cons ilis1 ilists)) (ans knil)) ; N-ary case
(receive (cars+ans cdrs) (%cars+cdrs+ ilists ans)
(if (null? cars+ans) ans ; Done.
(lp cdrs (apply kons cars+ans)))))
(let lp ((ilis ilis1) (ans knil)) ; Fast path
(if (null-ilist? ilis) ans
(lp (icdr ilis) (kons (icar ilis) ans))))))
(define (ifold-right kons knil ilis1 . ilists)
(check-arg procedure? kons ifold-right)
(if (pair? ilists)
(let recur ((ilists (cons ilis1 ilists))) ; N-ary case
(let ((cdrs (%cdrs ilists)))
(if (null? cdrs) knil
(apply kons (%cars+ ilists (recur cdrs))))))
(let recur ((ilis ilis1)) ; Fast path
(if (null? ilis) knil
(let ((head (icar ilis)))
(kons head (recur (icdr ilis))))))))
(define (ipair-fold-right f zero ilis1 . ilists)
(check-arg procedure? f ipair-fold-right)
(if (pair? ilists)
(let recur ((ilists (cons ilis1 ilists))) ; N-ary case
(let ((cdrs (%cdrs ilists)))
(if (null? cdrs) zero
(apply f (append ilists (list (recur cdrs)))))))
(let recur ((ilis ilis1)) ; Fast path
(if (null-ilist? ilis) zero (f ilis (recur (icdr ilis)))))))
(define (ipair-fold f zero ilis1 . ilists)
(check-arg procedure? f ipair-fold)
(if (pair? ilists)
(let lp ((ilists (cons ilis1 ilists)) (ans zero)) ; N-ary case
(let ((tails (%cdrs ilists)))
(if (null? tails) ans
(lp tails (apply f (append ilists (list ans)))))))
(let lp ((ilis ilis1) (ans zero))
(if (null-ilist? ilis) ans
(let ((tail (icdr ilis))) ; Grab the icdr now,
(lp tail (f ilis ans))))))) ; in case F SET-CDR!s LIS.
;;; IREDUCE and IREDUCE-RIGHT only use RIDENTITY in the empty-ilist case.
;;; These cannot meaningfully be n-ary.
(define (ireduce f ridentity ilis)
(check-arg procedure? f ireduce)
(if (null-ilist? ilis) ridentity
(ifold f (icar ilis) (icdr ilis))))
(define (ireduce-right f ridentity ilis)
(check-arg procedure? f ireduce-right)
(if (null-ilist? ilis) ridentity
(let recur ((head (icar ilis)) (ilis (icdr ilis)))
(if (ipair? ilis)
(f head (recur (icar ilis) (icdr ilis)))
head))))
;;; Mappers: iappend-map ipair-for-each ifilter-map imap-in-order
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (iappend-map f ilis1 . ilists)
(really-iappend-map iappend-map iappend f ilis1 ilists))
(define (really-iappend-map who appender f ilis1 ilists)
(check-arg procedure? f who)
(if (pair? ilists)
(receive (cars cdrs) (%cars+cdrs (cons ilis1 ilists))
(if (null? cars) '()
(let recur ((cars cars) (cdrs cdrs))
(let ((vals (apply f cars)))
(receive (cars2 cdrs2) (%cars+cdrs cdrs)
(if (null? cars2) vals
(appender vals (recur cars2 cdrs2))))))))
;; Fast path
(if (null-ilist? ilis1) '()
(let recur ((elt (icar ilis1)) (rest (icdr ilis1)))
(let ((vals (f elt)))
(if (null-ilist? rest) vals
(appender vals (recur (icar rest) (icdr rest)))))))))
(define (ipair-for-each proc ilis1 . ilists)
(check-arg procedure? proc ipair-for-each)
(if (pair? ilists)
(let lp ((ilists (cons ilis1 ilists)))
(let ((itails (%cdrs ilists)))
(if (pair? itails)
(begin (apply proc ilists)
(lp itails)))))
;; Fast path.
(let lp ((ilis ilis1))
(if (not (null-ilist? ilis))
(let ((tail (icdr ilis))) ; Grab the icdr now,
(proc ilis) ; even though nothing can happen
(lp tail))))))
;;; We stop when LIS1 runs out, not when any ilist runs out.
;;; Map F across L, and save up all the non-false results.
(define (ifilter-map f ilis1 . ilists)
(check-arg procedure? f ifilter-map)
(if (pair? ilists)
(let recur ((ilists (cons ilis1 ilists)))
(receive (cars cdrs) (%cars+cdrs ilists)
(if (pair? cars)
(cond ((apply f cars) => (lambda (x) (ipair x (recur cdrs))))
(else (recur cdrs))) ; Tail call in this arm.
'())))
;; Fast path.
(let recur ((ilis ilis1))
(if (null-ilist? ilis) ilis
(let ((tail (recur (icdr ilis))))
(cond ((f (icar ilis)) => (lambda (x) (ipair x tail)))
(else tail)))))))
;;; Map F across lists, guaranteeing to go left-to-right.
(define (imap-in-order f lis1 . lists)
(check-arg procedure? f imap-in-order)
(if (pair? lists)
(let recur ((lists (cons lis1 lists)))
(receive (cars cdrs) (%cars+cdrs lists)
(if (pair? cars)
(let ((x (apply f cars))) ; Do head first,
(ipair x (recur cdrs))) ; then tail.
'())))
;; Fast path.
(let recur ((lis lis1))
(if (null-ilist? lis) lis
(let ((tail (icdr lis))
(x (f (icar lis)))) ; Do head ifirst,
(ipair x (recur tail))))))) ; then tail.
;;; We extend IMAP to handle arguments of unequal ilength.
(define imap imap-in-order)
;;; ifilter, iremove, ipartition
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; IFILTER, IREMOVE, IPARTITION do not
;;; disorder the elements of their argument.
;; This IFILTER shares the longest tail of L that has no deleted elements.
;; If Scheme had multi-continuation calls, they could be made more efficient.
(define (ifilter pred lis) ; Sleazing with EQ? makes this
(check-arg procedure? pred ifilter) ; one faster.
(let recur ((lis lis))
(if (null-ilist? lis) lis ; Use NOT-IPAIR? to handle dotted lists.
(let ((head (icar lis))
(tail (icdr lis)))
(if (pred head)
(let ((new-tail (recur tail))) ; Replicate the RECUR call so
(if (eq? tail new-tail) lis
(ipair head new-tail)))
(recur tail)))))) ; this one can be a tail call.
;;; Another version that shares longest tail.
;(define (ifilter pred lis)
; (receive (ans no-del?)
; ;; (recur l) returns L with (pred x) values filtered.
; ;; It also returns a flag NO-DEL? if the returned value
; ;; is EQ? to L, i.e. if it didn't have to delete anything.
; (let recur ((l l))
; (if (null-ilist? l) (values l #t)
; (let ((x (icar l))
; (tl (icdr l)))
; (if (pred x)
; (receive (ans no-del?) (recur tl)
; (if no-del?
; (values l #t)
; (values (ipair x ans) #f)))
; (receive (ans no-del?) (recur tl) ; Delete X.
; (values ans #f))))))
; ans))
;;; Answers share common tail with LIS where possible;
;;; the technique is slightly subtle.
(define (ipartition pred lis)
(check-arg procedure? pred ipartition)
(let recur ((lis lis))
(if (null-ilist? lis) (values lis lis) ; Use NOT-IPAIR? to handle dotted lists.
(let ((elt (icar lis))
(tail (icdr lis)))
(receive (in out) (recur tail)
(if (pred elt)
(values (if (ipair? out) (ipair elt in) lis) out)
(values in (if (ipair? in) (ipair elt out) lis))))))))
;;; Inline us, please.
(define (iremove pred l) (ifilter (lambda (x) (not (pred x))) l))
;;; Here's the taxonomy for the IDELETE/IASSOC/IMEMBER functions.
;;; (I don't actually think these are the world's most important
;;; functions -- the procedural IFILTER/IREMOVE/IFIND/IFIND-TAIL variants
;;; are far more general.)
;;;
;;; Function Action
;;; ---------------------------------------------------------------------------
;;; iremove pred lis Delete by general predicate
;;; idelete x lis [=] Delete by element comparison
;;;
;;; ifind pred lis Search by general predicate
;;; ifind-tail pred lis Search by general predicate
;;; imember x lis [=] Search by element comparison
;;;
;;; iassoc key lis [=] Search alist by key comparison
;;; ialist-delete key alist [=] Alist-idelete by key comparison
(define (idelete x lis . maybe-=)
(let ((= (:optional maybe-= equal?)))
(ifilter (lambda (y) (not (= x y))) lis)))
;;; Extended from R4RS to take an optional comparison argument.
(define (imember x lis . maybe-=)
(let ((= (:optional maybe-= equal?)))
(ifind-tail (lambda (y) (= x y)) lis)))
;;; The IMEMBER and then IFIND-TAIL call should definitely
;;; be inlined for IMEMQ & IMEMV.
(define (imemq x lis) (imember x lis eq?))
(define (imemv x lis) (imember x lis eqv?))
;;; right-duplicate deletion
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; idelete-duplicates
;;;
;;; Beware -- these are N^2 algorithms. To efficiently iremove duplicates
;;; in long lists, sort the ilist to bring duplicates together, then use a
;;; linear-time algorithm to kill the dups. Or use an algorithm based on
;;; element-marking. The former gives you O(n lg n), the latter is linear.
(define (idelete-duplicates lis . maybe-=)
(let ((elt= (:optional maybe-= equal?)))
(check-arg procedure? elt= idelete-duplicates)
(let recur ((lis lis))
(if (null-ilist? lis) lis
(let* ((x (icar lis))
(tail (icdr lis))
(new-tail (recur (idelete x tail elt=))))
(if (eq? tail new-tail) lis (ipair x new-tail)))))))
;;; alist stuff
;;;;;;;;;;;;;;;
;;; Extended from R4RS to itake an optional comparison argument.
(define (iassoc x lis . maybe-=)
(let ((= (:optional maybe-= equal?)))
(ifind (lambda (entry) (= x (icar entry))) lis)))
(define (ialist-cons key datum alist) (ipair (ipair key datum) alist))
(define (alist-copy alist)
(imap (lambda (elt) (ipair (icar elt) (icdr elt)))
alist))
(define (ialist-delete key alist . maybe-=)
(let ((= (:optional maybe-= equal?)))
(ifilter (lambda (elt) (not (= key (icar elt)))) alist)))
;;; ifind ifind-tail itake-while idrop-while ispan ibreak iany ievery ilist-index
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (ifind pred ilist)
(cond ((ifind-tail pred ilist) => icar)
(else #f)))
(define (ifind-tail pred ilist)
(check-arg procedure? pred ifind-tail)
(let lp ((ilist ilist))
(and (not (null-ilist? ilist))
(if (pred (icar ilist)) ilist
(lp (icdr ilist))))))
(define (itake-while pred lis)
(check-arg procedure? pred itake-while)
(let recur ((lis lis))
(if (null-ilist? lis) '()
(let ((x (icar lis)))
(if (pred x)
(ipair x (recur (icdr lis)))
'())))))
(define (idrop-while pred lis)
(check-arg procedure? pred idrop-while)
(let lp ((lis lis))
(if (null-ilist? lis) '()
(if (pred (icar lis))
(lp (icdr lis))
lis))))
(define (ispan pred lis)
(check-arg procedure? pred ispan)
(let recur ((lis lis))
(if (null-ilist? lis) (values '() '())
(let ((x (icar lis)))
(if (pred x)
(receive (prefix suffix) (recur (icdr lis))
(values (ipair x prefix) suffix))
(values '() lis))))))
(define (ibreak pred lis) (ispan (lambda (x) (not (pred x))) lis))
(define (ievery pred lis1 . lists)
(check-arg procedure? pred ievery)
(if (pair? lists)
;; N-ary case
(receive (heads tails) (%cars+cdrs (ipair lis1 lists))
(or (not (ipair? heads))
(let lp ((heads heads) (tails tails))
(receive (next-heads next-tails) (%cars+cdrs tails)
(if (ipair? next-heads)
(and (apply pred heads) (lp next-heads next-tails))
(apply pred heads)))))) ; Last PRED app is tail call.
;; Fast path
(or (null-ilist? lis1)
(let lp ((head (icar lis1)) (tail (icdr lis1)))
(if (null-ilist? tail)
(pred head) ; Last PRED app is tail call.
(and (pred head) (lp (icar tail) (icdr tail))))))))
(define (iany pred ilis1 . ilists)
(check-arg procedure? pred iany)
(if (pair? ilists)
;; N-ary case
(receive (heads tails) (%cars+cdrs (cons ilis1 ilists))
(and (pair? heads)
(let lp ((heads heads) (tails tails))
(receive (next-heads next-tails) (%cars+cdrs tails)
(if (pair? next-heads)
(or (apply pred heads) (lp next-heads next-tails))
(apply pred heads)))))) ; Last PRED app is tail call.
;; Fast path
(and (not (null-ilist? ilis1))
(let lp ((head (icar ilis1)) (tail (icdr ilis1)))
(if (null-ilist? tail)
(pred head) ; Last PRED app is tail call.
(or (pred head) (lp (icar tail) (icdr tail))))))))
(define (ilist-index pred lis1 . lists)
(check-arg procedure? pred ilist-index)
(if (pair? lists)
;; N-ary case
(let lp ((lists (cons lis1 lists)) (n 0))
(receive (heads tails) (%cars+cdrs lists)
(and (pair? heads)
(if (apply pred heads) n
(lp tails (+ n 1))))))
;; Fast path
(let lp ((lis lis1) (n 0))
(and (not (null-ilist? lis))
(if (pred (icar lis)) n (lp (icdr lis) (+ n 1)))))))
;;; Reverse
;;;;;;;;;;;
(define (ireverse lis) (ifold ipair '() lis)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a117.sls 0000664 0000000 0000000 00000001036 14737542645 0020025 0 ustar 00root root 0000000 0000000 #!r6rs
(library (srfi :117)
(export
make-list-queue list-queue list-queue-copy list-queue-unfold list-queue-unfold-right
list-queue? list-queue-empty?
list-queue-front list-queue-back list-queue-list list-queue-first-last
list-queue-add-front! list-queue-add-back! list-queue-remove-front! list-queue-remove-back!
list-queue-remove-all! list-queue-set-list!
list-queue-append list-queue-append! list-queue-concatenate
list-queue-map list-queue-map! list-queue-for-each
)
(import (srfi :117 list-queues))
)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a117/ 0000775 0000000 0000000 00000000000 14737542645 0017302 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a117/list-queues-impl.scm 0000664 0000000 0000000 00000016720 14737542645 0023233 0 ustar 00root root 0000000 0000000 ;;;; Implementation of list-queue SRFI
;;;;
;;;; Copyright (C) John Cowan (2015, 2016). All Rights Reserved.
;;;;
;;;; Permission is hereby granted, free of charge, to any person obtaining a copy of
;;;; this software and associated documentation files (the "Software"), to deal in
;;;; the Software without restriction, including without limitation the rights to
;;;; use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
;;;; of the Software, and to permit persons to whom the Software is furnished to do
;;;; so, subject to the following conditions:
;;;;
;;;; The above copyright notice and this permission notice shall be included in all
;;;; copies or substantial portions of the Software.
;;;;
;;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
;;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
;;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;;;; SOFTWARE.
;;;; Later modified from the R7RS SRFI-117 reference implementation for
;;;; possible inclusion within the R6RS https://github.com/arcfide/chez-srfi.
;;; R7RS shims. Comment these out on an R7RS system.
;;; [John Cowan] stole this code from Chibi Scheme, which is BSD-licensed.
(define (make-list n . o)
(let ((default (if (pair? o) (car o))))
(let lp ((n n) (res '()))
(if (<= n 0) res (lp (- n 1) (cons default res))))))
(define (list-copy ls)
(let lp ((ls ls) (res '()))
(if (pair? ls)
(lp (cdr ls) (cons (car ls) res))
(append (reverse res) ls))))
(define (list-set! ls k x)
(cond ((null? ls) (error "invalid list index"))
((zero? k) (set-car! ls x))
(else (list-set! (cdr ls) (- k 1) x))))
;;; This definition is from Chibi's SRFI-1 implementation.
(define (last-pair ls) (if (null? (cdr ls)) ls (last-pair (cdr ls))))
;;; This definition of map! isn't fully SRFI-1 compliant, as it
;;; handles only unary functions. You can use SRFI-1's definition
;;; if you want.
(define (map! f lis)
(let lp ((lis lis))
(if (pair? lis)
(begin
(set-car! lis (f (car lis)))
(lp (cdr lis))))))
;;; The list-queue record
;;; The invariant is that either first is (the first pair of) a list
;;; and last is the last pair, or both of them are the empty list.
(define-record-type
( raw-make-list-queue list-queue?)
(fields
(mutable first get-first set-first!)
(mutable last get-last set-last!)))
;;; Constructors
(define make-list-queue
(case-lambda
((list)
(if (null? list)
(raw-make-list-queue '() '())
(raw-make-list-queue list (last-pair list))))
((list last)
(raw-make-list-queue list last))))
(define (list-queue . objs)
(make-list-queue objs))
(define (list-queue-copy list-queue)
(make-list-queue (list-copy (get-first list-queue))))
;;; Predicates
(define (list-queue-empty? list-queue)
(null? (get-first list-queue)))
;;; Accessors
(define (list-queue-front list-queue)
(if (list-queue-empty? list-queue)
(error "Empty list-queue")
(car (get-first list-queue))))
(define (list-queue-back list-queue)
(if (list-queue-empty? list-queue)
(error "Empty list-queue")
(car (get-last list-queue))))
;;; Mutators (which carefully maintain the invariant)
(define (list-queue-add-front! list-queue elem)
(let ((new-first (cons elem (get-first list-queue))))
(if (list-queue-empty? list-queue)
(set-last! list-queue new-first))
(set-first! list-queue new-first)))
(define (list-queue-add-back! list-queue elem)
(let ((new-last (list elem)))
(if (list-queue-empty? list-queue)
(set-first! list-queue new-last)
(set-cdr! (get-last list-queue) new-last))
(set-last! list-queue new-last)))
(define (list-queue-remove-front! list-queue)
(if (list-queue-empty? list-queue)
(error "Empty list-queue"))
(let* ((old-first (get-first list-queue))
(elem (car old-first))
(new-first (cdr old-first)))
(if (null? new-first)
(set-last! list-queue '()))
(set-first! list-queue new-first)
elem))
(define (list-queue-remove-back! list-queue)
(if (list-queue-empty? list-queue)
(error "Empty list-queue"))
(let* ((old-last (get-last list-queue))
(elem (car old-last))
(new-last (penult-pair (get-first list-queue))))
(if (null? new-last)
(set-first! list-queue '())
(set-cdr! new-last '()))
(set-last! list-queue new-last)
elem))
(define (list-queue-remove-all! list-queue)
(let ((result (get-first list-queue)))
(set-first! list-queue '())
(set-last! list-queue '())
result))
;; Return the next to last pair of lis, or nil if there is none
(define (penult-pair lis)
(let lp ((lis lis))
(cond
;((null? lis) (error "Empty list-queue"))
((null? (cdr lis)) '())
((null? (cddr lis)) lis)
(else (lp (cdr lis))))))
;;; The whole list-queue
;; Because append does not copy its back argument, we cannot use it
(define (list-queue-append . list-queues)
(list-queue-concatenate list-queues))
(define (list-queue-concatenate list-queues)
(let ((result (list-queue)))
(for-each
(lambda (list-queue)
(for-each (lambda (elem) (list-queue-add-back! result elem)) (get-first list-queue)))
list-queues)
result))
(define list-queue-append!
(case-lambda
(() (list-queue))
((queue) queue)
(queues
(for-each (lambda (queue) (list-queue-join! (car queues) queue))
(cdr queues))
(car queues))))
; Forcibly join two queues, destroying the second
(define (list-queue-join! queue1 queue2)
(set-cdr! (get-last queue1) (get-first queue2)))
(define (list-queue-map proc list-queue)
(make-list-queue (map proc (get-first list-queue))))
(define list-queue-unfold
(case-lambda
((stop? mapper successor seed queue)
(list-queue-unfold* stop? mapper successor seed queue))
((stop? mapper successor seed)
(list-queue-unfold* stop? mapper successor seed (list-queue)))))
(define (list-queue-unfold* stop? mapper successor seed queue)
(let loop ((seed seed))
(if (not (stop? seed))
(list-queue-add-front! (loop (successor seed)) (mapper seed)))
queue))
(define list-queue-unfold-right
(case-lambda
((stop? mapper successor seed queue)
(list-queue-unfold-right* stop? mapper successor seed queue))
((stop? mapper successor seed)
(list-queue-unfold-right* stop? mapper successor seed (list-queue)))))
(define (list-queue-unfold-right* stop? mapper successor seed queue)
(let loop ((seed seed))
(if (not (stop? seed))
(list-queue-add-back! (loop (successor seed)) (mapper seed)))
queue))
(define (list-queue-map! proc list-queue)
(map! proc (get-first list-queue)))
(define (list-queue-for-each proc list-queue)
(for-each proc (get-first list-queue)))
;;; Conversion
(define (list-queue-list list-queue)
(get-first list-queue))
(define (list-queue-first-last list-queue)
(values (get-first list-queue) (get-last list-queue)))
(define list-queue-set-list!
(case-lambda
((list-queue first)
(set-first! list-queue first)
(if (null? first)
(set-last! list-queue '())
(set-last! list-queue (last-pair first))))
((list-queue first last)
(set-first! list-queue first)
(set-last! list-queue last))))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a117/list-queues.sls 0000664 0000000 0000000 00000001341 14737542645 0022304 0 ustar 00root root 0000000 0000000 (library (srfi :117 list-queues)
(export
make-list-queue list-queue list-queue-copy list-queue-unfold list-queue-unfold-right
list-queue? list-queue-empty?
list-queue-front list-queue-back list-queue-list list-queue-first-last
list-queue-add-front! list-queue-add-back! list-queue-remove-front! list-queue-remove-back!
list-queue-remove-all! list-queue-set-list!
list-queue-append list-queue-append! list-queue-concatenate
list-queue-map list-queue-map! list-queue-for-each)
(import
(only (srfi :23) error)
(except (rnrs base) error)
(rnrs control)
(rnrs mutable-pairs)
(rnrs records syntactic)
(srfi private include))
(include/resolve ("srfi" "%3a117") "list-queues-impl.scm"))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a125.sls 0000664 0000000 0000000 00000002161 14737542645 0020024 0 ustar 00root root 0000000 0000000 (library (srfi :125)
(export
make-hash-table hash-table hash-table-unfold alist->hash-table
hash-table? hash-table-contains? hash-table-empty? hash-table=?
hash-table-mutable?
hash-table-ref hash-table-ref/default
hash-table-set! hash-table-delete! hash-table-intern! hash-table-update!
hash-table-update!/default hash-table-pop! hash-table-clear!
hash-table-size hash-table-keys hash-table-values hash-table-entries
hash-table-find hash-table-count
hash-table-map hash-table-for-each hash-table-map! hash-table-map->list
hash-table-fold hash-table-prune!
hash-table-copy hash-table-empty-copy hash-table->alist
hash-table-union! hash-table-intersection! hash-table-difference!
hash-table-xor!
;; The following procedures are deprecated by SRFI 125:
deprecated:hash deprecated:string-hash deprecated:string-ci-hash
deprecated:hash-by-identity
deprecated:hash-table-equivalence-function
deprecated:hash-table-hash-function deprecated:hash-table-exists?
deprecated:hash-table-walk deprecated:hash-table-merge!)
(import (srfi :125 hashtables)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a125/ 0000775 0000000 0000000 00000000000 14737542645 0017301 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a125/125.body.scm 0000664 0000000 0000000 00000046576 14737542645 0021272 0 ustar 00root root 0000000 0000000 ;;; Copyright 2015 William D Clinger.
;;;
;;; Permission to copy this software, in whole or in part, to use this
;;; software for any lawful purpose, and to redistribute this software
;;; is granted subject to the restriction that all copies made of this
;;; software must include this copyright and permission notice in full.
;;;
;;; I also request that you send me a copy of any improvements that you
;;; make to this software so that they may be incorporated within it to
;;; the benefit of the Scheme community.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Private stuff, not exported.
;;; Ten of the SRFI 125 procedures are deprecated, and another
;;; two allow alternative arguments that are deprecated.
(define (issue-deprecated-warnings?) #t)
(define (issue-warning-deprecated name-of-deprecated-misfeature)
(if (not (memq name-of-deprecated-misfeature already-warned))
(begin
(set! already-warned
(cons name-of-deprecated-misfeature already-warned))
(if (issue-deprecated-warnings?)
(let ((out (current-error-port)))
(display "WARNING: " out)
(display name-of-deprecated-misfeature out)
(newline out)
(display " is deprecated by SRFI 125. See" out)
(newline out)
(display " " out)
(display url:deprecated out)
(newline out))))))
(define url:deprecated
"http://srfi.schemers.org/srfi-125/srfi-125.html")
; List of deprecated features for which a warning has already
; been issued.
(define already-warned '())
;;; Comparators contain a type test predicate, which implementations
;;; of the hash-table-set! procedure can use to reject invalid keys.
;;; That's hard to do without sacrificing interoperability with R6RS
;;; and/or SRFI 69 and/or SRFI 126 hash tables.
;;;
;;; Full interoperability means the hash tables implemented here are
;;; interchangeable with the SRFI 126 hashtables used to implement them.
;;; SRFI 69 and R6RS and SRFI 126 hashtables don't contain comparators,
;;; so any association between a hash table and its comparator would have
;;; to be maintained outside the representation of hash tables themselves,
;;; which is problematic unless weak pointers are available.
;;;
;;; Not all of the hash tables implemented here will have comparators
;;; associated with them anyway, because an equivalence procedure
;;; and hash function can be used to create a hash table instead of
;;; a comparator (although that usage is deprecated by SRFI 125).
;;;
;;; One way to preserve interoperability while enforcing a comparator's
;;; type test is to incorporate that test into a hash table's hash
;;; function. The advantage of doing that should be weighed against
;;; these disadvantages:
;;;
;;; If the type test is slow, then hashing would also be slower.
;;;
;;; The R6RS, SRFI 69, and SRFI 126 APIs allow extraction of
;;; a hash function from some hash tables.
;;; Some programmers might expect that hash function to be the
;;; hash function encapsulated by the comparator (in the sense
;;; of eq?, perhaps) even though this API makes no such guarantee
;;; (and extraction of that hash function from an existing hash
;;; table can only be done by calling a deprecated procedure).
;;; If %enforce-comparator-type-tests is true, then make-hash-table,
;;; when passed a comparator, will use a hash function that enforces
;;; the comparator's type test.
(define %enforce-comparator-type-tests #t)
;;; Given a comparator, return its hash function, possibly augmented
;;; by the comparator's type test.
(define (%comparator-hash-function comparator)
(let ((okay? (comparator-type-test-predicate comparator))
(hash-function (comparator-hash-function comparator)))
(if %enforce-comparator-type-tests
(lambda (x . rest)
(cond ((not (okay? x))
(error #f "key rejected by hash-table comparator"
x
comparator))
((null? rest)
(hash-function x))
(else
(apply hash-function x rest))))
hash-function)))
;;; A unique (in the sense of eq?) value that will never be found
;;; within a hash-table.
(define %not-found (list '%not-found))
;;; A unique (in the sense of eq?) value that escapes only as an irritant
;;; when a hash-table key is not found.
(define %not-found-irritant (list 'not-found))
;;; The error message used when a hash-table key is not found.
(define %not-found-message "hash-table key not found")
;;; We let SRFI 126 decide which weakness is supported
(define (%check-optional-arguments procname args)
(if (memq 'thread-safe args)
(error (string-append (symbol->string procname)
": unsupported optional argument(s)")
args)))
(define (%get-hash-table-weakness args)
(cond
((memq 'ephemeral-values args)
(if (or (memq 'ephemeral-keys args)
(memq 'weak-keys args))
'ephemeral-key-and-value
'ephemeral-value))
((memq 'ephemeral-keys args)
(if (memq 'weak-values args)
'ephemeral-key-and-value
'ephemeral-key))
((memq 'weak-keys args)
(if (memq 'weak-values args)
'weak-key-and-value
'weak-key))
((memq 'weak-values args)
'weak-value)
(else #f)))
(define (%get-hash-table-capacity args)
(find fixnum? args))
;;; This was exported by an earlier draft of SRFI 125,
;;; and is still used by hash-table=?
(define (hash-table-every proc ht)
(call-with-values
(lambda () (hashtable-entries ht))
(lambda (keys vals)
(let ((size (vector-length keys)))
(let loop ((i 0))
(or (fx>=? i size)
(let* ((key (vector-ref keys i))
(val (vector-ref vals i)))
(and (proc key val)
(loop (fx+ i 1))))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Exported procedures
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Constructors.
;;; The first argument can be a comparator or an equality predicate.
;;;
;;; If the first argument is a comparator, any remaining arguments
;;; are implementation-dependent, but a non-negative exact integer
;;; should be interpreted as an initial capacity and the symbols
;;; thread-safe, weak-keys, ephemeral-keys, weak-values, and
;;; emphemeral-values should be interpreted specially. (These
;;; special symbols are distinct from the analogous special symbols
;;; in SRFI 126.)
;;;
;;; If the first argument is not a comparator, then it had better
;;; be an equality predicate (which is deprecated by SRFI 125).
;;; If a second argument is present and is a procedure, then it's
;;; a hash function (which is allowed only for the deprecated case
;;; in which the first argument is an equality predicate). If a
;;; second argument is not a procedure, then it's some kind of
;;; implementation-dependent optional argument, as are all arguments
;;; beyond the second.
;;;
;;; SRFI 128 defines make-eq-comparator, make-eqv-comparator, and
;;; make-equal-comparator procedures whose hash function is the
;;; default-hash procedure of SRFI 128, which is inappropriate
;;; for use with eq? and eqv? unless the object being hashed is
;;; never mutated. Neither SRFI 125 nor 128 provide any way to
;;; define a comparator whose hash function is truly compatible
;;; with the use of eq? or eqv? as an equality predicate.
;;;
;;; That would make SRFI 125 almost as bad as SRFI 69 if not for
;;; the following paragraph of SRFI 125:
;;;
;;; Implementations are permitted to ignore user-specified
;;; hash functions in certain circumstances. Specifically,
;;; if the equality predicate, whether passed as part of a
;;; comparator or explicitly, is more fine-grained (in the
;;; sense of R7RS-small section 6.1) than equal?, the
;;; implementation is free — indeed, is encouraged — to
;;; ignore the user-specified hash function and use something
;;; implementation-dependent. This allows the use of addresses
;;; as hashes, in which case the keys must be rehashed if
;;; they are moved by the garbage collector. Such a hash
;;; function is unsafe to use outside the context of
;;; implementation-provided hash tables. It can of course be
;;; exposed by an implementation as an extension, with
;;; suitable warnings against inappropriate uses.
;;;
;;; That gives implementations permission to do something more
;;; useful, but when should implementations take advantage of
;;; that permission? This implementation uses the superior
;;; solution provided by SRFI 126 whenever:
;;;
;;; A comparator is passed as first argument and its equality
;;; predicate is eq? or eqv?.
;;;
;;; The eq? or eqv? procedure is passed as first argument
;;; (which is a deprecated usage).
(define (make-hash-table comparator/equiv . rest)
(if (comparator? comparator/equiv)
(let ((equiv (comparator-equality-predicate comparator/equiv))
(hash-function (%comparator-hash-function comparator/equiv)))
(%make-hash-table equiv hash-function rest))
(let* ((equiv comparator/equiv)
(hash-function (if (and (not (null? rest))
(procedure? (car rest)))
(car rest)
#f))
(rest (if hash-function (cdr rest) rest)))
(issue-warning-deprecated 'srfi-69-style:make-hash-table)
(%make-hash-table equiv hash-function rest))))
(define (%make-hash-table equiv hash-function opts)
(%check-optional-arguments 'make-hash-table opts)
(let ((weakness (%get-hash-table-weakness opts))
(capacity (%get-hash-table-capacity opts)))
;; Use SRFI :126 make-hashtable to handle capacity and weakness
(cond ((equal? equiv eq?)
(make-eq-hashtable capacity weakness))
((equal? equiv eqv?)
(make-eqv-hashtable capacity weakness))
(hash-function
(make-hashtable hash-function equiv capacity weakness))
((equal? equiv equal?)
(make-hashtable equal-hash equiv capacity weakness))
((equal? equiv string=?)
(make-hashtable string-hash equiv capacity weakness))
((equal? equiv string-ci=?)
(make-hashtable string-ci-hash equiv capacity weakness))
((equal? equiv symbol=?)
(make-hashtable symbol-hash equiv capacity weakness))
(else
(error "make-hash-table: unable to infer hash function"
equiv)))))
(define (hash-table comparator . rest)
(let ((ht (apply make-hash-table comparator rest)))
(let loop ((kvs rest))
(cond
((null? kvs) #f)
((null? (cdr kvs)) (error #f "hash-table: wrong number of arguments"))
((hashtable-contains? ht (car kvs))
(error "hash-table: two equivalent keys were provided"
(car kvs)))
(else (hashtable-set! ht (car kvs) (cadr kvs))
(loop (cddr kvs)))))
(hash-table-copy ht #f)))
(define (hash-table-unfold stop? mapper successor seed comparator . rest)
(let ((ht (apply make-hash-table comparator rest)))
(let loop ((seed seed))
(if (stop? seed)
ht
(call-with-values
(lambda () (mapper seed))
(lambda (key val)
(hash-table-set! ht key val)
(loop (successor seed))))))))
(define (alist->hash-table alist comparator/equiv . rest)
(if (and (not (null? rest))
(procedure? (car rest)))
(issue-warning-deprecated 'srfi-69-style:alist->hash-table))
(let ((ht (apply make-hash-table comparator/equiv rest))
(entries (reverse alist)))
(for-each (lambda (entry)
(hash-table-set! ht (car entry) (cdr entry)))
entries)
ht))
;;; Predicates.
;; (define (hash-table? obj)
;; (hashtable? obj))
;; (define (hash-table-contains? ht key)
;; (hashtable-contains? ht key))
;; (define (hash-table-empty? ht)
;; (hashtable-empty? ht))
(define (hash-table=? value-comparator ht1 ht2)
(let ((val=? (comparator-equality-predicate value-comparator))
(n1 (hash-table-size ht1))
(n2 (hash-table-size ht2)))
(and (= n1 n2)
(eq? (hashtable-equivalence-function ht1)
(hashtable-equivalence-function ht2))
(hash-table-every (lambda (key val1)
(and (hash-table-contains? ht2 key)
(val=? val1
(hashtable-ref ht2 key 'ignored))))
ht1))))
(define (hash-table-mutable? ht)
(hashtable-mutable? ht))
;;; Accessors.
(define hash-table-ref
(case-lambda
((ht key) (hashtable-ref ht key))
((ht key failure)
(let ((val (hashtable-ref ht key %not-found)))
(if (eq? val %not-found)
(failure)
val)))
((ht key failure success)
(let ((val (hashtable-ref ht key %not-found)))
(if (eq? val %not-found)
(failure)
(success val))))))
(define (hash-table-ref/default ht key default)
(hashtable-ref ht key default))
;;; Mutators.
(define hash-table-set!
(case-lambda
((ht) #f)
((ht key val) (hashtable-set! ht key val))
((ht key1 val1 key2 val2 . others)
(hashtable-set! ht key1 val1)
(hashtable-set! ht key2 val2)
(apply hash-table-set! ht others))))
(define (hash-table-delete! ht . keys)
(let ((count 0))
(for-each (lambda (key)
(when (hashtable-contains? ht key)
(set! count (fx+ 1 count))
(hashtable-delete! ht key)))
keys)
count))
;; (define (hash-table-intern! ht key failure)
;; (hashtable-intern! ht key failure))
(define hash-table-update!
(case-lambda
((ht key updater)
(hashtable-update! ht key updater))
((ht key updater failure)
(let ((updater* (lambda (val)
(if (eq? %not-found val)
(updater (failure))
(updater val)))))
(hashtable-update! ht key updater* %not-found)))
((ht key updater failure success)
(let* ((updater* (lambda (val)
(if (eq? %not-found val)
(updater (failure))
(success (updater val))))))
(hashtable-update! ht key updater* %not-found)))))
(define (hash-table-update!/default ht key updater default)
(hashtable-update! ht key updater default))
;; (define (hash-table-pop! ht)
;; (hashtable-pop! ht))
;; (define (hash-table-clear! ht)
;; (hashtable-clear! ht))
;;; The whole hash table.
;; (define (hash-table-size ht)
;; (hashtable-size ht))
(define (hash-table-keys ht)
(vector->list (hashtable-keys ht)))
(define (hash-table-values ht)
(vector->list (hashtable-values ht)))
(define (hash-table-entries ht)
(call-with-values
(lambda () (hashtable-entries ht))
(lambda (keys vals)
(values (vector->list keys)
(vector->list vals)))))
(define (hash-table-find proc ht failure)
(call-with-values
(lambda () (hashtable-entries ht))
(lambda (keys vals)
(let ((size (vector-length keys)))
(let loop ((i 0))
(if (fx>=? i size)
(failure)
(let* ((key (vector-ref keys i))
(val (vector-ref vals i))
(x (proc key val)))
(or x (loop (fx+ i 1))))))))))
(define (hash-table-count pred ht)
(let ((count 0))
(call-with-values
(lambda () (hashtable-entries ht))
(lambda (keys vals)
(vector-for-each (lambda (key val)
(if (pred key val) (set! count (fx+ count 1))))
keys vals)))
count))
;;; Mapping and folding.
(define (hash-table-map proc comparator ht)
(let ((result (make-hash-table comparator)))
(hash-table-for-each
(lambda (key val)
(hash-table-set! result key (proc val)))
ht)
result))
(define (hash-table-map->list proc ht)
(call-with-values
(lambda () (hash-table-entries ht))
(lambda (keys vals)
(map proc keys vals))))
;;; With this particular implementation, the proc can safely mutate ht.
;;; That property is not guaranteed by the specification, but can be
;;; relied upon by procedures defined in this file.
(define (hash-table-for-each proc ht)
(hashtable-walk ht proc))
(define (hash-table-map! proc ht)
(hashtable-update-all! ht proc))
(define (hash-table-fold proc init ht)
(if (hashtable? proc)
(deprecated:hash-table-fold proc init ht)
(hashtable-sum ht init proc)))
(define (hash-table-prune! proc ht)
(hashtable-prune! ht proc))
;;; Copying and conversion.
;; (define hash-table-copy hashtable-copy)
(define (hash-table-empty-copy ht)
(let* ((ht2 (hash-table-copy ht #t))
(ignored (hash-table-clear! ht2)))
ht2))
(define (hash-table->alist ht)
(call-with-values
(lambda () (hash-table-entries ht))
(lambda (keys vals)
(map cons keys vals))))
;;; Hash tables as sets.
(define (hash-table-union! ht1 ht2)
(hash-table-for-each
(lambda (key2 val2)
(if (not (hashtable-contains? ht1 key2))
(hashtable-set! ht1 key2 val2)))
ht2)
ht1)
(define (hash-table-intersection! ht1 ht2)
(hash-table-for-each
(lambda (key1 val1)
(if (not (hashtable-contains? ht2 key1))
(hashtable-delete! ht1 key1)))
ht1)
ht1)
(define (hash-table-difference! ht1 ht2)
(hash-table-for-each
(lambda (key1 val1)
(if (hashtable-contains? ht2 key1)
(hashtable-delete! ht1 key1)))
ht1)
ht1)
(define (hash-table-xor! ht1 ht2)
(hash-table-for-each
(lambda (key2 val2)
(if (hashtable-contains? ht1 key2)
(hashtable-delete! ht1 key2)
(hashtable-set! ht1 key2 val2)))
ht2)
ht1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; The following procedures are deprecated by SRFI 125, but must
;;; be exported nonetheless.
;;;
;;; Programs that import the (srfi 125) library must rename the
;;; deprecated string-hash and string-ci-hash procedures to avoid
;;; conflict with the string-hash and string-ci-hash procedures
;;; exported by SRFI 126 and SRFI 128.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (deprecated:hash obj . rest)
(issue-warning-deprecated 'hash)
(default-hash obj))
(define (deprecated:string-hash obj . rest)
(issue-warning-deprecated 'srfi-125:string-hash)
(string-hash obj))
(define (deprecated:string-ci-hash obj . rest)
(issue-warning-deprecated 'srfi-125:string-ci-hash)
(string-ci-hash obj))
(define (deprecated:hash-by-identity obj . rest)
(issue-warning-deprecated 'hash-by-identity)
(deprecated:hash obj))
(define (deprecated:hash-table-equivalence-function ht)
(issue-warning-deprecated 'hash-table-equivalence-function)
(hashtable-equivalence-function ht))
(define (deprecated:hash-table-hash-function ht)
(issue-warning-deprecated 'hash-table-hash-function)
(hashtable-hash-function ht))
(define (deprecated:hash-table-exists? ht key)
(issue-warning-deprecated 'hash-table-exists?)
(hash-table-contains? ht key))
(define (deprecated:hash-table-walk ht proc)
(issue-warning-deprecated 'hash-table-walk)
(hash-table-for-each proc ht))
(define (deprecated:hash-table-fold ht proc seed)
(issue-warning-deprecated 'srfi-69-style:hash-table-fold)
(hash-table-fold proc seed ht))
(define (deprecated:hash-table-merge! ht1 ht2)
(issue-warning-deprecated 'hash-table-merge!)
(hash-table-union! ht1 ht2))
; eof
chez-srfi-0.0+git20241031.b424440+dfsg/%3a125/hashtables.sls 0000664 0000000 0000000 00000004136 14737542645 0022146 0 ustar 00root root 0000000 0000000 (library (srfi :125 hashtables)
(export
make-hash-table hash-table hash-table-unfold alist->hash-table
hash-table? hash-table-contains? hash-table-empty? hash-table=?
hash-table-mutable?
hash-table-ref hash-table-ref/default
hash-table-set! hash-table-delete! hash-table-intern! hash-table-update!
hash-table-update!/default hash-table-pop! hash-table-clear!
hash-table-size hash-table-keys hash-table-values hash-table-entries
hash-table-find hash-table-count
hash-table-map hash-table-for-each hash-table-map! hash-table-map->list
hash-table-fold hash-table-prune!
hash-table-copy hash-table-empty-copy hash-table->alist
hash-table-union! hash-table-intersection! hash-table-difference!
hash-table-xor!
;; The following procedures are deprecated by SRFI 125:
deprecated:hash
deprecated:string-hash
deprecated:string-ci-hash
deprecated:hash-by-identity
deprecated:hash-table-equivalence-function
deprecated:hash-table-hash-function
deprecated:hash-table-exists?
deprecated:hash-table-walk
deprecated:hash-table-merge!)
(import (except (rnrs)
make-hashtable hashtable-clear! hashtable-copy
hashtable-ref hashtable-update! make-eq-hashtable
make-eqv-hashtable)
(srfi private include)
(rename (srfi :126)
(hashtable? hash-table?)
(hashtable-contains? hash-table-contains?)
(hashtable-empty? hash-table-empty?)
(hashtable-intern! hash-table-intern!)
(hashtable-clear! hash-table-clear!)
(hashtable-copy hash-table-copy)
(hashtable-size hash-table-size)
(hashtable-pop! hash-table-pop!)
(hashtable-merge! hash-table-merge!)
(hashtable-hash-function hash-table-hash-function)
(hashtable-equivalence-function hash-table-equivalence-function))
(except (srfi :128) hash-salt string-hash string-ci-hash symbol-hash))
(include/resolve ("srfi" "%3a125") "125.body.scm"))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a126.sls 0000664 0000000 0000000 00000001700 14737542645 0020023 0 ustar 00root root 0000000 0000000 (library (srfi :126)
(export make-eq-hashtable make-eqv-hashtable make-hashtable
alist->eq-hashtable alist->eqv-hashtable alist->hashtable weakness
hashtable? hashtable-size hashtable-ref hashtable-set!
hashtable-delete! hashtable-contains? hashtable-lookup
hashtable-update! hashtable-intern! hashtable-copy hashtable-clear!
hashtable-empty-copy hashtable-keys hashtable-values
hashtable-entries hashtable-key-list hashtable-value-list
hashtable-entry-lists hashtable-walk hashtable-update-all!
hashtable-prune! hashtable-merge! hashtable-sum hashtable-map->lset
hashtable-find hashtable-empty? hashtable-pop! hashtable-inc!
hashtable-dec! hashtable-equivalence-function hashtable-hash-function
hashtable-weakness hashtable-mutable? hash-salt equal-hash
string-hash string-ci-hash symbol-hash)
(import (srfi :126 r6rs-hashtables)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a126/ 0000775 0000000 0000000 00000000000 14737542645 0017302 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a126/126.body.scm 0000664 0000000 0000000 00000031150 14737542645 0021252 0 ustar 00root root 0000000 0000000 (define make-eq-hashtable
(case-lambda
(() (rnrs:make-eq-hashtable))
((capacity)
(if capacity
(rnrs:make-eq-hashtable capacity)
(rnrs:make-eq-hashtable)))
((capacity weakness)
(if weakness
(cond
((memq weakness (weak-eq-hashtables-supported))
(if capacity
((make-weak-eq-hashtable-procedure weakness) capacity)
((make-weak-eq-hashtable-procedure weakness))))
((memq weakness (ephemeral-eq-hashtables-supported))
(if capacity
((make-ephemeral-eq-hashtable-procedure weakness) capacity)
((make-ephemeral-eq-hashtable-procedure weakness))))
(else (error 'make-eq-hashtable "weakness not supported" weakness)))
(if capacity
(rnrs:make-eq-hashtable capacity)
(rnrs:make-eq-hashtable))))))
(define make-eqv-hashtable
(case-lambda
(() (rnrs:make-eqv-hashtable))
((capacity)
(if capacity
(rnrs:make-eqv-hashtable capacity)
(rnrs:make-eqv-hashtable)))
((capacity weakness)
(if weakness
(cond
((memq weakness (weak-eqv-hashtables-supported))
(if capacity
((make-weak-eqv-hashtable-procedure weakness) capacity)
((make-weak-eqv-hashtable-procedure weakness))))
((memq weakness (ephemeral-eqv-hashtables-supported))
(if capacity
((make-ephemeral-eqv-hashtable-procedure weakness) capacity)
((make-ephemeral-eqv-hashtable-procedure weakness))))
(else (error 'make-eqv-hashtable "weakness not supported" weakness)))
(if capacity
(rnrs:make-eqv-hashtable capacity)
(rnrs:make-eqv-hashtable))))))
(define make-hashtable
(case-lambda
((hash equiv)
(if hash
(rnrs:make-hashtable (if (pair? hash) (car hash) hash) equiv)
(cond
((eq? equiv eq?) (make-eq-hashtable))
((eq? equiv eqv?) (make-eqv-hashtable))
(else (error 'make-hashtable
"hash procedure cannot be #f except with eq? or eqv?"
hash equiv)))))
((hash equiv capacity)
(if hash
(if capacity
(rnrs:make-hashtable (if (pair? hash) (car hash) hash) equiv
capacity)
(rnrs:make-hashtable (if (pair? hash) (car hash) hash) equiv))
(cond
((eq? equiv eq?) (make-eq-hashtable capacity))
((eq? equiv eqv?) (make-eqv-hashtable capacity))
(else (error 'make-hashtable
"hash procedure cannot be #f except with eq? or eqv?"
hash equiv)))))
((hash equiv capacity weakness)
(if hash
(let ((hash (if (pair? hash) (car hash) hash))) ;; why? - read spec
(if weakness
(cond
((memq weakness (weak-hashtables-supported))
(if capacity
((make-weak-hashtable-procedure weakness) hash equiv
capacity)
((make-weak-hashtable-procedure weakness) hash equiv)))
((memq weakness (ephemeral-hashtables-supported))
(if capacity
((make-ephemeral-hashtable-procedure weakness) hash equiv
capacity)
((make-ephemeral-hashtable-procedure weakness) hash equiv)))
(else (error 'make-hashtable "weakness not supported" weakness)))
(if capacity
(rnrs:make-hashtable hash equiv capacity)
(rnrs:make-hashtable hash equiv))))
(cond ; hash function not provided
((eq? equiv eq?)
(make-eq-hashtable capacity weakness))
((eq? equiv eqv?)
(make-eqv-hashtable capacity weakness))
(else (error 'make-hashtable
"hash procedure cannot be #f except with eq? or eqv?"
hash equiv)))))))
(define (alist->eq-hashtable . args)
(apply alist->hashtable #f eq? args))
(define (alist->eqv-hashtable . args)
(apply alist->hashtable #f eqv? args))
(define alist->hashtable
(case-lambda
((hash equiv alist)
(alist->hashtable hash equiv #f #f alist))
((hash equiv capacity alist)
(alist->hashtable hash equiv capacity #f alist))
((hash equiv capacity weakness alist)
(let ((hashtable (make-hashtable hash equiv capacity weakness)))
(for-each (lambda (entry)
(hashtable-set! hashtable (car entry) (cdr entry)))
(reverse alist))
hashtable))))
(define-enumeration weakness
(weak-key
weak-value
weak-key-and-value
ephemeral-key
ephemeral-value
ephemeral-key-and-value)
weakness-set)
#;(define hashtable? rnrs:hashtable?)
#;(define hashtable-size rnrs:hashtable-size)
(define nil (cons #f #f))
(define (nil? obj) (eq? obj nil))
(define hashtable-ref
(case-lambda
((hashtable key)
(let ((value (rnrs:hashtable-ref hashtable key nil)))
(if (nil? value)
(error "No such key in hashtable." hashtable key)
value)))
((hashtable key default)
(rnrs:hashtable-ref hashtable key default))))
#;(define hashtable-set! rnrs:hashtable-set!)
#;(define hashtable-delete! rnrs:hashtable-delete!)
#;(define hashtable-contains? rnrs:hashtable-contains?)
(define (hashtable-lookup hashtable key)
(let ((value (rnrs:hashtable-ref hashtable key nil)))
(if (nil? value)
(values #f #f)
(values value #t))))
(define hashtable-update!
(case-lambda
((hashtable key proc)
(rnrs:hashtable-update! hashtable key
(lambda (value)
(if (nil? value)
(error "No such key in hashtable."
hashtable key)
(proc value)))
nil))
((hashtable key proc default)
(rnrs:hashtable-update! hashtable key proc default))))
(define (hashtable-intern! hashtable key default-proc)
(if (hashtable-cell-support)
(let ((cell (hashtable-cell hashtable key nil)))
(if (nil? (hashtable-cell-value cell))
(let ((value (default-proc)))
(set-hashtable-cell-value! cell value)
value)
(hashtable-cell-value cell)))
(let ((value (rnrs:hashtable-ref hashtable key nil)))
(if (nil? value)
(let ((value (default-proc)))
(hashtable-set! hashtable key value)
value)
value))))
(define hashtable-copy
(case-lambda
((hashtable) (hashtable-copy hashtable #f #f))
((hashtable mutable) (hashtable-copy hashtable mutable #f))
((hashtable mutable weakness)
(when weakness
(error 'hashtable-copy "No weak or ephemeral tables supported."))
(rnrs:hashtable-copy hashtable mutable))))
(define hashtable-clear!
(case-lambda
((hashtable) (rnrs:hashtable-clear! hashtable))
((hashtable capacity)
(if capacity
(cond-expand
(ikarus (rnrs:hashtable-clear! hashtable))
(else (rnrs:hashtable-clear! hashtable capacity)))
(rnrs:hashtable-clear! hashtable)))))
(define hashtable-empty-copy
(case-lambda
((hashtable) (hashtable-empty-copy hashtable #f))
((hashtable capacity)
(make-hashtable (hashtable-hash-function hashtable)
(hashtable-equivalence-function hashtable)
(if (eq? #t capacity)
(hashtable-size hashtable)
capacity)
(hashtable-weakness hashtable)))))
#;(define hashtable-keys rnrs:hashtable-keys)
;;; Defined in helpers.sls
;; (define (hashtable-values hashtable)
;; (let-values (((keys values) (hashtable-entries hashtable)))
;; values))
#;(define hashtable-entries rnrs:hashtable-entries)
(define (hashtable-key-list hashtable)
(hashtable-map->lset hashtable (lambda (key value) key)))
(define (hashtable-value-list hashtable)
(hashtable-map->lset hashtable (lambda (key value) value)))
(define (hashtable-entry-lists hashtable)
(let ((keys '())
(vals '()))
(hashtable-walk hashtable
(lambda (key val)
(set! keys (cons key keys))
(set! vals (cons val vals))))
(values keys vals)))
;;; XXX The procedures hashtable-walk, hashtable-update-all!, hashtable-prune!,
;;; and hashtable-sum should be implemented more efficiently at the platform
;;; level. In particular, they should not allocate intermediate vectors or
;;; lists to hold the keys or values that are being operated on.
(define (hashtable-walk hashtable proc)
(let-values (((keys values) (hashtable-entries hashtable)))
(vector-for-each proc keys values)))
(define (hashtable-update-all! hashtable proc)
(let-values (((keys values) (hashtable-entries hashtable)))
(vector-for-each (lambda (key value)
(hashtable-set! hashtable key (proc key value)))
keys values)))
(define (hashtable-prune! hashtable proc)
(let-values (((keys values) (hashtable-entries hashtable)))
(vector-for-each (lambda (key value)
(when (proc key value)
(hashtable-delete! hashtable key)))
keys values)))
(define (hashtable-merge! hashtable-dest hashtable-source)
(let-values (((keys values) (hashtable-entries hashtable-source)))
(vector-for-each (lambda (key value)
(hashtable-set! hashtable-dest key value))
keys values))
hashtable-dest)
(define (hashtable-sum hashtable init proc)
(let-values (((keys vals) (hashtable-entries hashtable)))
(let ((size (vector-length keys)))
(let loop ((i 0) (result init))
(if (fx>=? i size)
result
(loop (fx+ i 1) (proc (vector-ref keys i)
(vector-ref vals i)
result)))))))
(define (hashtable-map->lset hashtable proc)
(let-values (((keys vals) (hashtable-entries hashtable)))
(let ((size (vector-length keys)))
(let loop ((i 0) (accumulator '()))
(if (fx>=? i size)
accumulator
(loop (fx+ i 1) (cons (proc (vector-ref keys i) (vector-ref vals i))
accumulator)))))))
;;; XXX If available, let-escape-continuation might be more efficient than
;;; call/cc here.
(define (hashtable-find hashtable proc)
(call/cc
(lambda (return)
(hashtable-walk hashtable
(lambda (key value)
(when (proc key value)
(return key value #t))))
(return #f #f #f))))
(define (hashtable-empty? hashtable)
(fxzero? (hashtable-size hashtable)))
;;; XXX A platform-level implementation could avoid allocating the constant true
;;; function and the lookup for the key in the delete operation.
(define (hashtable-pop! hashtable)
(if (hashtable-empty? hashtable)
(error "Cannot pop from empty hashtable." hashtable)
(let-values (((key value found?)
(hashtable-find hashtable (lambda (k v) #t))))
(hashtable-delete! hashtable key)
(values key value))))
(define hashtable-inc!
(case-lambda
((hashtable key) (hashtable-inc! hashtable key 1))
((hashtable key number)
(hashtable-update! hashtable key (lambda (v) (+ v number)) 0))))
(define hashtable-dec!
(case-lambda
((hashtable key) (hashtable-dec! hashtable key 1))
((hashtable key number)
(hashtable-update! hashtable key (lambda (v) (- v number)) 0))))
#;(define hashtable-equivalence-function rnrs:hashtable-equivalence-function)
#;(define hashtable-hash-function rnrs-hashtable-hash-function)
;;; Defined in helpers.sls
#;(define (hashtable-weakness hashtable) #f)
#;(define hashtable-mutable? rnrs-hashtable-mutable?)
(define *hash-salt*
(let ((seed (get-environment-variable "SRFI_126_HASH_SEED")))
(if (or (not seed) (string=? seed ""))
(random-integer (greatest-fixnum))
(mod (string-hash seed) (greatest-fixnum)))))
(define (hash-salt) *hash-salt*)
#;(define equal-hash rnrs-equal-hash)
#;(define string-hash rnrs-string-hash)
#;(define string-ci-hash rnrs-string-ci-hash)
#;(define symbol-hash rnrs-symbol-hash)
;; Local Variables:
;; eval: (put 'hashtable-walk 'scheme-indent-function 1)
;; eval: (put 'hashtable-update-all! 'scheme-indent-function 1)
;; eval: (put 'hashtable-prune! 'scheme-indent-function 1)
;; eval: (put 'hashtable-sum 'scheme-indent-function 2)
;; eval: (put 'hashtable-map->lset 'scheme-indent-function 1)
;; eval: (put 'hashtable-find 'scheme-indent-function 1)
;; End:
chez-srfi-0.0+git20241031.b424440+dfsg/%3a126/helpers/ 0000775 0000000 0000000 00000000000 14737542645 0020744 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a126/helpers/helpers.chezscheme.sls 0000664 0000000 0000000 00000011004 14737542645 0025242 0 ustar 00root root 0000000 0000000 (library (srfi :126 helpers helpers)
(export make-weak-eq-hashtable-procedure weak-eq-hashtables-supported
make-weak-eqv-hashtable-procedure weak-eqv-hashtables-supported
make-weak-hashtable-procedure weak-hashtables-supported
make-ephemeral-eq-hashtable-procedure ephemeral-eq-hashtables-supported
make-ephemeral-eqv-hashtable-procedure
ephemeral-eqv-hashtables-supported
make-ephemeral-hashtable-procedure ephemeral-hashtables-supported
hashtable-values hashtable-weakness
hashtable-cell-support hashtable-cell hashtable-cell-key
hashtable-cell-value set-hashtable-cell-value!
get-environment-variable random-integer)
(import (rename (chezscheme)
(getenv get-environment-variable)))
(define (make-weak-eq-hashtable-procedure weakness)
(if (eq? weakness 'weak-key)
make-weak-eq-hashtable
(error 'make-weak-eq-hashtable "weakness not supported"
weakness)))
(define-syntax weak-eq-hashtables-supported
(syntax-rules ()
((weak-eq-hashtables-supported) '(weak-key))))
(define (make-weak-eqv-hashtable-procedure weakness)
(if (eq? weakness 'weak-key)
make-weak-eqv-hashtable
(error 'make-weak-eqv-hashtable "weakness not supported"
weakness)))
(define-syntax weak-eqv-hashtables-supported
(syntax-rules ()
((weak-eqv-hashtables-supported) '(weak-key))))
(define (make-weak-hashtable-procedure weakness)
(error 'make-weak-hashtable "weak hashtables not supported"))
(define-syntax weak-hashtables-supported
(syntax-rules ()
((weak-hashtables-supported) '())))
(meta-cond
((let-values (((major minor sub-minor) (scheme-version-number)))
(or (> major 9)
(and (= major 9) (>= minor 5))))
;; has ephemeral eq- and eqv-hashtables
(define (make-ephemeral-eq-hashtable-procedure weakness)
(if (eq? weakness 'ephemeral-key)
make-ephemeron-eq-hashtable
(error 'make-ephemeral-eq-hashtable "weakness not supported"
weakness)))
(define-syntax ephemeral-eq-hashtables-supported
(syntax-rules ()
((ephemeral-eq-hashtables-supported) '(ephemeral-key))))
(define (make-ephemeral-eqv-hashtable-procedure weakness)
(if (eq? weakness 'ephemeral-key)
make-ephemeron-eqv-hashtable
(error 'make-ephemeral-eqv-hashtable "weakness not supported"
weakness)))
(define-syntax ephemeral-eqv-hashtables-supported
(syntax-rules ()
((ephemeral-eqv-hashtables-supported) '(ephemeral-key))))
(define (make-ephemeral-hashtable-procedure weakness)
(error 'make-ephemeral-hashtable "weakness not supported" weakness))
(define-syntax ephemeral-hashtables-supported
(syntax-rules ()
((ephemeral-hashtables-supported) '(ephemeral-key))))
(define (hashtable-weakness hashtable)
(cond ((hashtable-weak? hashtable) 'weak-key)
((hashtable-ephemeron? hashtable) 'ephemeral-key)
(else #f))))
(else ; no ephemeral hashtables
(define (make-ephemeral-eq-hashtable-procedure weakness)
(error 'make-ephemeral-hashtable "ephemeral eq hashtables not supported"))
(define-syntax ephemeral-eq-hashtables-supported
(syntax-rules ()
((ephemeral-eq-hashtables-supported) '())))
(define (make-ephemeral-eqv-hashtable-procedure weakness)
(error 'make-ephemeral-hashtable "ephemeral eqv hashtables not supported"))
(define-syntax ephemeral-eqv-hashtables-supported
(syntax-rules ()
((ephemeral-eqv-hashtables-supported) '())))
(define (make-ephemeral-hashtable-procedure weakness)
(error 'make-ephemeral-hashtable "ephemeral hashtables not supported"))
(define-syntax ephemeral-hashtables-supported
(syntax-rules ()
((ephemeral-hashtables-supported) '())))
(define (hashtable-weakness hashtable)
(cond ((hashtable-weak? hashtable) 'weak-key)
(else #f)))))
;; Support for hashtable cells
(define-syntax hashtable-cell-support
(syntax-rules ()
((hashtable-cell-support) #t)))
(define hashtable-cell-key car)
(define hashtable-cell-value cdr)
(define set-hashtable-cell-value! set-cdr!)
(define (random-integer seed)
(fxmod (fxxor (random seed)
(fx* 3 (fxdiv (random (time-nanosecond (current-time)))
4)))
seed)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a126/helpers/helpers.sls 0000664 0000000 0000000 00000005737 14737542645 0023145 0 ustar 00root root 0000000 0000000 (library (srfi :126 helpers helpers)
(export make-weak-eq-hashtable-procedure weak-eq-hashtables-supported
make-weak-eqv-hashtable-procedure weak-eqv-hashtables-supported
make-weak-hashtable-procedure weak-hashtables-supported
make-ephemeral-eq-hashtable-procedure ephemeral-eq-hashtables-supported
make-ephemeral-eqv-hashtable-procedure
ephemeral-eqv-hashtables-supported
make-ephemeral-hashtable-procedure ephemeral-hashtables-supported
hashtable-values hashtable-weakness
hashtable-cell-support hashtable-cell hashtable-cell-key
hashtable-cell-value set-hashtable-cell-value!
get-environment-variable random-integer)
(import (rnrs) (srfi :39)
(only (srfi :27) random-integer)
(only (srfi :98) get-environment-variable))
(define (make-weak-eq-hashtable-procedure weakness)
(error 'make-weak-eq-hashtable "weak eq hashtables not supported"))
(define-syntax weak-eq-hashtables-supported
(syntax-rules ()
((weak-eq-hashtables-supported) '())))
(define (make-weak-eqv-hashtable-procedure weakness)
(error 'make-weak-eqv-hashtable "weak eqv hashtables not supported"))
(define-syntax weak-eqv-hashtables-supported
(syntax-rules ()
((weak-eqv-hashtables-supported) '())))
(define (make-weak-hashtable-procedure weakness)
(error 'make-weak-hashtable "weak hashtables not supported"))
(define-syntax weak-hashtables-supported
(syntax-rules ()
((weak-hashtables-supported) '())))
(define (make-ephemeral-eq-hashtable-procedure weakness)
(error 'make-ephemeral-hashtable "ephemeral eq hashtables not supported"))
(define-syntax ephemeral-eq-hashtables-supported
(syntax-rules ()
((ephemeral-eq-hashtables-supported) '())))
(define (make-ephemeral-eqv-hashtable-procedure weakness)
(error 'make-ephemeral-hashtable "ephemeral eqv hashtables not supported"))
(define-syntax ephemeral-eqv-hashtables-supported
(syntax-rules ()
((ephemeral-eqv-hashtables-supported) '())))
(define (make-ephemeral-hashtable-procedure weakness)
(error 'make-ephemeral-hashtable "ephemeral hashtables not supported"))
(define-syntax ephemeral-hashtables-supported
(syntax-rules ()
((ephemeral-hashtables-supported) '())))
(define (hashtable-values hashtable)
(let-values (((keys values) (hashtable-entries hashtable)))
values))
(define (hashtable-weakness hashtable) #f)
(define-syntax hashtable-cell-support
(syntax-rules ()
((hashtable-cell-support) #f)))
(define (hashtable-cell hashtable)
(error 'hashtable-cell "hashtable cells not supported"))
(define (hashtable-cell-key cell)
(error 'hashtable-cell "hashtable cells not supported"))
(define (hashtable-cell-value cell)
(error 'hashtable-cell "hashtable cells not supported"))
(define (set-hashtable-cell-value! cell value)
(error 'hashtable-cell "hashtable cells not supported")))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a126/r6rs-hashtables.sls 0000664 0000000 0000000 00000002764 14737542645 0023046 0 ustar 00root root 0000000 0000000 (library (srfi :126 r6rs-hashtables)
(export make-eq-hashtable make-eqv-hashtable make-hashtable
alist->eq-hashtable alist->eqv-hashtable alist->hashtable weakness
hashtable? hashtable-size hashtable-ref hashtable-set!
hashtable-delete! hashtable-contains? hashtable-lookup
hashtable-update! hashtable-intern! hashtable-copy hashtable-clear!
hashtable-empty-copy hashtable-keys hashtable-values
hashtable-entries hashtable-key-list hashtable-value-list
hashtable-entry-lists hashtable-walk hashtable-update-all!
hashtable-prune! hashtable-merge! hashtable-sum hashtable-map->lset
hashtable-find hashtable-empty? hashtable-pop! hashtable-inc!
hashtable-dec! hashtable-equivalence-function hashtable-hash-function
hashtable-weakness hashtable-mutable? hash-salt equal-hash
string-hash string-ci-hash symbol-hash)
(import (rename (rnrs)
(make-eq-hashtable rnrs:make-eq-hashtable)
(make-eqv-hashtable rnrs:make-eqv-hashtable)
(make-hashtable rnrs:make-hashtable)
(hashtable-ref rnrs:hashtable-ref)
(hashtable-update! rnrs:hashtable-update!)
(hashtable-copy rnrs:hashtable-copy)
(hashtable-clear! rnrs:hashtable-clear!))
(srfi :0 cond-expand)
(srfi :126 helpers helpers)
(srfi private include))
(include/resolve ("srfi" "%3a126") "126.body.scm"))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a127.sls 0000664 0000000 0000000 00000000705 14737542645 0020030 0 ustar 00root root 0000000 0000000 (library (srfi :127)
(export generator->lseq lseq? lseq=?
lseq-car lseq-first lseq-cdr lseq-rest lseq-ref lseq-take lseq-drop
lseq-realize lseq->generator lseq-length lseq-append lseq-zip
lseq-map lseq-for-each lseq-filter lseq-remove
lseq-find lseq-find-tail lseq-take-while lseq-drop-while
lseq-any lseq-every lseq-index lseq-member lseq-memq lseq-memv)
(import (srfi :127 lazy-sequences)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a127/ 0000775 0000000 0000000 00000000000 14737542645 0017303 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a127/lazy-sequences.sls 0000664 0000000 0000000 00000001044 14737542645 0022775 0 ustar 00root root 0000000 0000000 (library (srfi :127 lazy-sequences)
(export generator->lseq lseq? lseq=?
lseq-car lseq-first lseq-cdr lseq-rest lseq-ref lseq-take lseq-drop
lseq-realize lseq->generator lseq-length lseq-append lseq-zip
lseq-map lseq-for-each lseq-filter lseq-remove
lseq-find lseq-find-tail lseq-take-while lseq-drop-while
lseq-any lseq-every lseq-index lseq-member lseq-memq lseq-memv)
(import (rnrs) (rnrs mutable-pairs) (srfi private include))
(include/resolve ("srfi" "%3a127") "lseqs-impl.scm"))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a127/lseqs-impl.scm 0000664 0000000 0000000 00000017265 14737542645 0022110 0 ustar 00root root 0000000 0000000 ;; Helper returns #t if any element of list is null or #f if none
(define (any-null? list)
(cond
((null? list) #f)
((null? (car list)) #t)
(else (any-null? (cdr list)))))
;; gappend procedure cloned from SRFI 121
(define (gappend . args)
(lambda () (if (null? args)
(eof-object)
(let loop ((v ((car args))))
(if (eof-object? v)
(begin (set! args (cdr args))
(if (null? args)
(eof-object)
(loop ((car args)))))
v)))))
;;; Convert a generator (procedure with no arguments) to an lseq
;;; This is the basic constructor for lseqs, since every proper list
;;; is already an lseq and so list->lseq is not needed
(define (generator->lseq gen)
(let ((value (gen)))
;; See what starts off the generator:
;; if it's already exhausted, the lseq is empty,
;; otherwise, return an improper list with one value and the generator
;; in the tail, which is how we represent unrealized lseqs
(if (eof-object? value)
'()
(cons value gen))))
;;; Car on lseqs is the same as on lists
(define (lseq-car lseq) (car lseq))
(define (lseq-first lseq) (car lseq))
;;; Lseq-cdr expands the generator if it's there, or falls back to regular cdr
(define (lseq-cdr lseq)
;; We assume lseq is a pair, because it is an error if it isn't
;; If it's a procedure, we assume it's a generator and invoke it
(if (procedure? (cdr lseq))
(let ((obj ((cdr lseq))))
(cond
;; If the generator is exhausted, replace it with () and return ()
((eof-object? obj)
(set-cdr! lseq '())
'())
;; Otherwise, make a new pair of the value and the generator
;; and patch it in to the cdr
(else (let ((result (cons obj (cdr lseq))))
(set-cdr! lseq result)
result))))
;; If there is no procedure, return the ordinary cdr
(cdr lseq)))
(define (lseq-rest lseq) (lseq-cdr lseq))
;;; Returns #t if argument is an lseq
;;; Note that without arity inspection, we can't be sure a procedure in the
;;; tail is really a generator, so we assume it is
(define (lseq? obj)
(cond
;; null list is a lseq
((null? obj) #t)
;; non-list is not an lseq
((not (pair? obj)) #f)
;; improper list with procedure in the tail is (presumed to be) an lseq
((procedure? (cdr obj)) #t)
;; otherwise keep looking
(else (lseq? (cdr obj)))))
;;; Compare lseqs for equality
(define (lseq=? = lseq1 lseq2)
(cond
((and (null? lseq1) (null? lseq2))
#t)
((or (null? lseq1) (null? lseq2))
#f)
((= (lseq-car lseq1) (lseq-car lseq2))
(lseq=? = (lseq-cdr lseq1) (lseq-cdr lseq2)))
(else #f)))
;;; Take the first n elements of lseq and return as a list
(define (lseq-take lseq i)
(generator->lseq
(lambda ()
(if (= i 0)
(eof-object)
(let ((result (lseq-car lseq)))
(set! lseq (lseq-cdr lseq))
(set! i (- i 1))
result)))))
;; Drop the first n arguments of lseq
;; No reason not to do it eagerly
(define (lseq-drop lseq i)
(let loop ((i i) (lseq lseq))
(if (= i 0)
lseq
(loop (- i 1) (lseq-cdr lseq)))))
;; Get the nth argument of lseq
(define (lseq-ref lseq i) (lseq-car (lseq-drop lseq i)))
;;; Convert lseq to a list by lseq-cdr-ing down it to the end
(define (lseq-realize lseq)
(let loop ((next lseq))
(if (null? next)
lseq
(loop (lseq-cdr next)))))
;;; Realize an lseq and return its length
(define (lseq-length lseq) (length (lseq-realize lseq)))
;; Return a generator that steps through the elements of the lseq
(define (lseq->generator lseq)
(lambda ()
(if (null? lseq)
(eof-object)
(let ((result (lseq-car lseq)))
(set! lseq (lseq-cdr lseq))
result))))
;; lseq-append converts lseqs to generators and gappends them
(define (lseq-append . lseqs)
(generator->lseq (apply gappend (map lseq->generator lseqs))))
;; Safe version of lseq-cdr that returns () if the argument is ()
(define (safe-lseq-cdr obj)
(if (null? obj)
obj
(lseq-cdr obj)))
;; Lazily map lseqs through a proc to produce another lseq
(define (lseq-map proc . lseqs)
(generator->lseq
(lambda ()
(if (any-null? lseqs)
(eof-object)
(let ((result (apply proc (map lseq-car lseqs))))
(set! lseqs (map safe-lseq-cdr lseqs))
result)))))
;; Zip cars of lseqs into a list and return an lseq of those lists
(define (lseq-zip . lseqs) (apply lseq-map list lseqs))
;; Eagerly apply a proc to the elements of lseqs
;; Included because it's a common operation, even though it is trivial
(define (lseq-for-each proc . lseqs)
(apply for-each proc (map lseq-realize lseqs)))
;; Filter an lseq lazily to include only elements that satisfy pred
(define (lseq-filter pred lseq)
(generator->lseq
(lambda ()
(let loop ((lseq1 lseq))
(if (null? lseq1)
(eof-object)
(let ((result (lseq-car lseq1)))
(cond
((pred result)
(set! lseq (lseq-cdr lseq1))
result)
(else
(loop (lseq-cdr lseq1))))))))))
;; Negated filter
(define (lseq-remove pred lseq)
(lseq-filter (lambda (x) (not (pred x))) lseq))
;; Find an element that satisfies a pred, or #f if no such element
(define (lseq-find pred lseq)
(cond
((null? lseq) #f)
((pred (lseq-car lseq)) (lseq-car lseq))
(else (lseq-find pred (lseq-cdr lseq)))))
;; Find the tail of an lseq whose car satisfies a pred, or #f if no such
(define (lseq-find-tail pred lseq)
(cond
((null? lseq) #f)
((pred (lseq-car lseq)) lseq)
(else (lseq-find-tail pred (lseq-cdr lseq)))))
;; Return initial elements of lseq that satisfy pred
(define (lseq-take-while pred lseq)
(generator->lseq
(lambda ()
(if (not (pred (lseq-car lseq)))
(eof-object)
(let ((result (lseq-car lseq)))
(set! lseq (lseq-cdr lseq))
result)))))
;; Return all but initial of lseq that satisfy pred
;; No reason not to do it eagerly
(define (lseq-drop-while pred lseq)
(let loop ((lseq lseq))
(if (not (pred (lseq-car lseq)))
lseq
(loop (lseq-cdr lseq)))))
;; Apply predicate across lseqs, returning result if it is true
(define (lseq-any pred . lseqs)
(let loop ((lseqs lseqs))
(if (any-null? lseqs)
#f
(let ((result (apply pred (map lseq-car lseqs))))
(if result
result
(loop (map lseq-cdr lseqs)))))))
;; Apply predicate across lseqs, returning false if predicate does
(define (lseq-every pred . lseqs)
(let loop ((lseqs lseqs) (last-result #t))
(if (any-null? lseqs)
last-result
(let ((result (apply pred (map lseq-car lseqs))))
(if result
(loop (map lseq-cdr lseqs) result)
#f)))))
;; Return the index of the first element of lseq that satisfies pred
(define (lseq-index pred . lseqs)
(let loop ((lseqs lseqs) (n 0))
(cond
((any-null? lseqs) #f)
((apply pred (map lseq-car lseqs)) n)
(else (loop (map safe-lseq-cdr lseqs) (+ n 1))))))
;; Return tail of lseq whose first element is x in the sense of = (default equal?)
(define lseq-member
(case-lambda
((x lseq) (lseq-member x lseq equal?))
((x lseq =) (cond
((null? lseq) #f)
((= x (lseq-car lseq)) lseq)
(else (lseq-member x (lseq-cdr lseq) =))))))
;; Member using eqv?
(define (lseq-memv x lseq) (lseq-member x lseq eqv?))
;; Member using eq?
(define (lseq-memq x lseq) (lseq-member x lseq eq?))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a128.sls 0000664 0000000 0000000 00000001340 14737542645 0020025 0 ustar 00root root 0000000 0000000 (library (srfi :128)
(export comparator? comparator-ordered? comparator-hashable? make-comparator
make-pair-comparator make-list-comparator make-vector-comparator
make-eq-comparator make-eqv-comparator make-equal-comparator
boolean-hash char-hash char-ci-hash string-hash string-ci-hash
symbol-hash number-hash make-default-comparator default-hash
comparator-register-default! comparator-type-test-predicate
comparator-equality-predicate comparator-ordering-predicate
comparator-hash-function comparator-test-type comparator-check-type
comparator-hash hash-bound hash-salt =? >? <=? >=?
comparator-if<=>)
(import (srfi :128 comparators)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a128/ 0000775 0000000 0000000 00000000000 14737542645 0017304 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a128/128.body1.scm 0000664 0000000 0000000 00000027752 14737542645 0021354 0 ustar 00root root 0000000 0000000 ;;; Copyright (C) John Cowan (2015). All Rights Reserved.
;;;
;;; Permission is hereby granted, free of charge, to any person
;;; obtaining a copy of this software and associated documentation
;;; files (the "Software"), to deal in the Software without
;;; restriction, including without limitation the rights to use,
;;; copy, modify, merge, publish, distribute, sublicense, and/or
;;; sell copies of the Software, and to permit persons to whom the
;;; Software is furnished to do so, subject to the following
;;; conditions:
;;;
;;; The above copyright notice and this permission notice shall be
;;; included in all copies or substantial portions of the Software.
;;;
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
;;; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
;;; OTHER DEALINGS IN THE SOFTWARE.
;;;; Main part of the SRFI 114 reference implementation
;;; "There are two ways of constructing a software design: One way is to
;;; make it so simple that there are obviously no deficiencies, and the
;;; other way is to make it so complicated that there are no *obvious*
;;; deficiencies." --Tony Hoare
;;; Syntax (because syntax must be defined before it is used, contra Dr. Hardcase)
;; Arithmetic if
(define-syntax comparator-if<=>
(syntax-rules ()
((if<=> a b less equal greater)
(comparator-if<=> (make-default-comparator) a b less equal greater))
((comparator-if<=> comparator a b less equal greater)
(cond
((=? comparator a b) equal)
(( comparator a b) less)
(else greater)))))
;; Upper bound of hash functions is 2^25-1
(define-syntax hash-bound
(syntax-rules ()
((hash-bound) 33554432)))
(define %salt% (make-parameter 16064047))
(define-syntax hash-salt
(syntax-rules ()
((hash-salt) (%salt%))))
(define-syntax with-hash-salt
(syntax-rules ()
((with-hash-salt new-salt hash-func obj)
(parameterize ((%salt% new-salt)) (hash-func obj)))))
;;; Definition of comparator records with accessors and basic comparator
(define-record-type comparator
(make-raw-comparator type-test equality ordering hash ordering? hash?)
comparator?
(type-test comparator-type-test-predicate)
(equality comparator-equality-predicate)
(ordering comparator-ordering-predicate)
(hash comparator-hash-function)
(ordering? comparator-ordered?)
(hash? comparator-hashable?))
;; Public constructor
(define (make-comparator type-test equality ordering hash)
(make-raw-comparator
(if (eq? type-test #t) (lambda (x) #t) type-test)
(if (eq? equality #t) (lambda (x y) (eqv? (ordering x y) 0)) equality)
(if ordering ordering (lambda (x y) (error #f "ordering not supported")))
(if hash hash (lambda (x y) (error #f "hashing not supported")))
(if ordering #t #f)
(if hash #t #f)))
;;; Invokers
;; Invoke the test type
(define (comparator-test-type comparator obj)
((comparator-type-test-predicate comparator) obj))
;; Invoke the test type and throw an error if it fails
(define (comparator-check-type comparator obj)
(if (comparator-test-type comparator obj)
#t
(error #f "comparator type check failed" comparator obj)))
;; Invoke the hash function
(define (comparator-hash comparator obj)
((comparator-hash-function comparator) obj))
;;; Comparison predicates
;; Binary versions for internal use
(define (binary=? comparator a b)
((comparator-equality-predicate comparator) a b))
(define (binary comparator a b)
((comparator-ordering-predicate comparator) a b))
(define (binary>? comparator a b)
(binary comparator b a))
(define (binary<=? comparator a b)
(not (binary>? comparator a b)))
(define (binary>=? comparator a b)
(not (binary comparator a b)))
;; General versions for export
(define (=? comparator a b . objs)
(let loop ((a a) (b b) (objs objs))
(and (binary=? comparator a b)
(if (null? objs) #t (loop b (car objs) (cdr objs))))))
(define ( comparator a b . objs)
(let loop ((a a) (b b) (objs objs))
(and (binary comparator a b)
(if (null? objs) #t (loop b (car objs) (cdr objs))))))
(define (>? comparator a b . objs)
(let loop ((a a) (b b) (objs objs))
(and (binary>? comparator a b)
(if (null? objs) #t (loop b (car objs) (cdr objs))))))
(define (<=? comparator a b . objs)
(let loop ((a a) (b b) (objs objs))
(and (binary<=? comparator a b)
(if (null? objs) #t (loop b (car objs) (cdr objs))))))
(define (>=? comparator a b . objs)
(let loop ((a a) (b b) (objs objs))
(and (binary>=? comparator a b)
(if (null? objs) #t (loop b (car objs) (cdr objs))))))
;;; Simple ordering and hash functions
(define (boolean a b)
;; #f < #t but not otherwise
(and (not a) b))
(define (boolean-hash obj)
(if obj (%salt%) 0))
(define (char-hash obj)
(modulo (* (%salt%) (char->integer obj)) (hash-bound)))
(define (char-ci-hash obj)
(modulo (* (%salt%) (char->integer (char-foldcase obj))) (hash-bound)))
(define (number-hash obj)
(cond
((nan? obj) (%salt%))
((and (infinite? obj) (positive? obj)) (* 2 (%salt%)))
((infinite? obj) (* (%salt%) 3))
((real? obj) (abs (exact (round obj))))
(else (+ (number-hash (real-part obj)) (number-hash (imag-part obj))))))
;; Lexicographic ordering of complex numbers
(define (complex a b)
(if (= (real-part a) (real-part b))
(< (imag-part a) (imag-part b))
(< (real-part a) (real-part b))))
;; already defined in (rnrs hashtables)
#;(define (string-ci-hash obj)
(string-hash (string-foldcase obj)))
(define (symbol a b) (string (symbol->string a) (symbol->string b)))
;; already defined in (rnrs hashtables)
#;(define (symbol-hash obj)
(string-hash (symbol->string obj)))
;;; Wrapped equality predicates
;;; These comparators don't have ordering functions.
(define (make-eq-comparator)
(make-comparator #t eq? #f default-hash))
(define (make-eqv-comparator)
(make-comparator #t eqv? #f default-hash))
(define (make-equal-comparator)
(make-comparator #t equal? #f default-hash))
;;; Sequence ordering and hash functions
;; The hash functions are based on djb2, but
;; modulo 2^25 instead of 2^32 in hopes of sticking to fixnums.
(define (make-hasher)
(let ((result (%salt%)))
(case-lambda
(() result)
((n) (set! result (+ (modulo (* result 33) (hash-bound)) n))
result))))
;;; Pair comparator
(define (make-pair-comparator car-comparator cdr-comparator)
(make-comparator
(make-pair-type-test car-comparator cdr-comparator)
(make-pair=? car-comparator cdr-comparator)
(make-pair car-comparator cdr-comparator)
(make-pair-hash car-comparator cdr-comparator)))
(define (make-pair-type-test car-comparator cdr-comparator)
(lambda (obj)
(and (pair? obj)
(comparator-test-type car-comparator (car obj))
(comparator-test-type cdr-comparator (cdr obj)))))
(define (make-pair=? car-comparator cdr-comparator)
(lambda (a b)
(and ((comparator-equality-predicate car-comparator) (car a) (car b))
((comparator-equality-predicate cdr-comparator) (cdr a) (cdr b)))))
(define (make-pair car-comparator cdr-comparator)
(lambda (a b)
(if (=? car-comparator (car a) (car b))
( cdr-comparator (cdr a) (cdr b))
( car-comparator (car a) (car b)))))
(define (make-pair-hash car-comparator cdr-comparator)
(lambda (obj)
(let ((acc (make-hasher)))
(acc (comparator-hash car-comparator (car obj)))
(acc (comparator-hash cdr-comparator (cdr obj)))
(acc))))
;;; List comparator
;; Cheap test for listness
(define (norp? obj) (or (null? obj) (pair? obj)))
(define (make-list-comparator element-comparator type-test empty? head tail)
(make-comparator
(make-list-type-test element-comparator type-test empty? head tail)
(make-list=? element-comparator type-test empty? head tail)
(make-list element-comparator type-test empty? head tail)
(make-list-hash element-comparator type-test empty? head tail)))
(define (make-list-type-test element-comparator type-test empty? head tail)
(lambda (obj)
(and
(type-test obj)
(let ((elem-type-test (comparator-type-test-predicate element-comparator)))
(let loop ((obj obj))
(cond
((empty? obj) #t)
((not (elem-type-test (head obj))) #f)
(else (loop (tail obj)))))))))
(define (make-list=? element-comparator type-test empty? head tail)
(lambda (a b)
(let ((elem=? (comparator-equality-predicate element-comparator)))
(let loop ((a a) (b b))
(cond
((and (empty? a) (empty? b) #t))
((empty? a) #f)
((empty? b) #f)
((elem=? (head a) (head b)) (loop (tail a) (tail b)))
(else #f))))))
(define (make-list element-comparator type-test empty? head tail)
(lambda (a b)
(let ((elem=? (comparator-equality-predicate element-comparator))
(elem (comparator-ordering-predicate element-comparator)))
(let loop ((a a) (b b))
(cond
((and (empty? a) (empty? b) #f))
((empty? a) #t)
((empty? b) #f)
((elem=? (head a) (head b)) (loop (tail a) (tail b)))
((elem (head a) (head b)) #t)
(else #f))))))
(define (make-list-hash element-comparator type-test empty? head tail)
(lambda (obj)
(let ((elem-hash (comparator-hash-function element-comparator))
(acc (make-hasher)))
(let loop ((obj obj))
(cond
((empty? obj) (acc))
(else (acc (elem-hash (head obj))) (loop (tail obj))))))))
;;; Vector comparator
(define (make-vector-comparator element-comparator type-test length ref)
(make-comparator
(make-vector-type-test element-comparator type-test length ref)
(make-vector=? element-comparator type-test length ref)
(make-vector element-comparator type-test length ref)
(make-vector-hash element-comparator type-test length ref)))
(define (make-vector-type-test element-comparator type-test length ref)
(lambda (obj)
(and
(type-test obj)
(let ((elem-type-test (comparator-type-test-predicate element-comparator))
(len (length obj)))
(let loop ((n 0))
(cond
((= n len) #t)
((not (elem-type-test (ref obj n))) #f)
(else (loop (+ n 1)))))))))
(define (make-vector=? element-comparator type-test length ref)
(lambda (a b)
(and
(= (length a) (length b))
(let ((elem=? (comparator-equality-predicate element-comparator))
(len (length b)))
(let loop ((n 0))
(cond
((= n len) #t)
((elem=? (ref a n) (ref b n)) (loop (+ n 1)))
(else #f)))))))
(define (make-vector element-comparator type-test length ref)
(lambda (a b)
(cond
((< (length a) (length b)) #t)
((> (length a) (length b)) #f)
(else
(let ((elem=? (comparator-equality-predicate element-comparator))
(elem (comparator-ordering-predicate element-comparator))
(len (length a)))
(let loop ((n 0))
(cond
((= n len) #f)
((elem=? (ref a n) (ref b n)) (loop (+ n 1)))
((elem (ref a n) (ref b n)) #t)
(else #f))))))))
(define (make-vector-hash element-comparator type-test length ref)
(lambda (obj)
(let ((elem-hash (comparator-hash-function element-comparator))
(acc (make-hasher))
(len (length obj)))
(let loop ((n 0))
(cond
((= n len) (acc))
(else (acc (elem-hash (ref obj n))) (loop (+ n 1))))))))
;; already defined in (rnrs hashtables)
#;(define (string-hash obj)
(let ((acc (make-hasher))
(len (string-length obj)))
(let loop ((n 0))
(cond
((= n len) (acc))
(else (acc (char->integer (string-ref obj n))) (loop (+ n 1)))))))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a128/128.body2.scm 0000664 0000000 0000000 00000012645 14737542645 0021350 0 ustar 00root root 0000000 0000000 ;;; Copyright (C) John Cowan (2015). All Rights Reserved.
;;;
;;; Permission is hereby granted, free of charge, to any person
;;; obtaining a copy of this software and associated documentation
;;; files (the "Software"), to deal in the Software without
;;; restriction, including without limitation the rights to use,
;;; copy, modify, merge, publish, distribute, sublicense, and/or
;;; sell copies of the Software, and to permit persons to whom the
;;; Software is furnished to do so, subject to the following
;;; conditions:
;;;
;;; The above copyright notice and this permission notice shall be
;;; included in all copies or substantial portions of the Software.
;;;
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
;;; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
;;; OTHER DEALINGS IN THE SOFTWARE.
;;; The default comparator
;;; Standard comparators and their functions
;; The unknown-object comparator, used as a fallback to everything else
;; Everything compares exactly the same and hashes to 0
(define unknown-object-comparator
(make-comparator
(lambda (obj) #t)
(lambda (a b) #t)
(lambda (a b) #f)
(lambda (obj) 0)))
;; Next index for added comparator
(define first-comparator-index 9)
(define *next-comparator-index* 9)
(define *registered-comparators* (list unknown-object-comparator))
;; Register a new comparator for use by the default comparator.
(define (comparator-register-default! comparator)
(set! *registered-comparators* (cons comparator *registered-comparators*))
(set! *next-comparator-index* (+ *next-comparator-index* 1)))
;; Return ordinal for object types: null sorts before pairs, which sort
;; before booleans, etc. Implementations can extend this.
;; People who call comparator-register-default! effectively do extend it.
(define (object-type obj)
(cond
((null? obj) 0)
((pair? obj) 1)
((boolean? obj) 2)
((char? obj) 3)
((string? obj) 4)
((symbol? obj) 5)
((number? obj) 6)
((vector? obj) 7)
((bytevector? obj) 8)
; Add more here if you want: be sure to update comparator-index variables
(else (registered-index obj))))
;; Return the index for the registered type of obj.
(define (registered-index obj)
(let loop ((i 0) (registry *registered-comparators*))
(cond
((null? registry) (+ first-comparator-index i))
((comparator-test-type (car registry) obj) (+ first-comparator-index i))
(else (loop (+ i 1) (cdr registry))))))
;; Given an index, retrieve a registered conductor.
;; Index must be >= first-comparator-index.
(define (registered-comparator i)
(list-ref *registered-comparators* (- i first-comparator-index)))
(define (dispatch-equality type a b)
(case type
((0) #t) ; All empty lists are equal
((1) ((make-pair=? (make-default-comparator) (make-default-comparator)) a b))
((2) (boolean=? a b))
((3) (char=? a b))
((4) (string=? a b))
((5) (symbol=? a b))
((6) (= a b))
((7) ((make-vector=? (make-default-comparator)
vector? vector-length vector-ref) a b))
((8) ((make-vector=? (make-comparator exact-integer? = < default-hash)
bytevector? bytevector-length bytevector-u8-ref) a b))
; Add more here
(else (binary=? (registered-comparator type) a b))))
(define (dispatch-ordering type a b)
(case type
((0) 0) ; All empty lists are equal
((1) ((make-pair (make-default-comparator) (make-default-comparator)) a b))
((2) (boolean a b))
((3) (char a b))
((4) (string a b))
((5) (symbol a b))
((6) (complex a b))
((7) ((make-vector (make-default-comparator) vector? vector-length vector-ref) a b))
((8) ((make-vector (make-comparator exact-integer? = < default-hash)
bytevector? bytevector-length bytevector-u8-ref) a b))
; Add more here
(else (binary (registered-comparator type) a b))))
;;; The author of SRFI 128 has suggested a post-finalization note
;;; saying the first and third bullet items stating "must" requirements
;;; for default-hash may be weakened. That allows a much faster hash
;;; function to be used for lists and vectors.
(define (default-hash obj)
(case (object-type obj)
((0 1 7) ; empty list, pair, or vector
((make-hasher) (equal-hash obj)))
((2) (boolean-hash obj))
((3) (char-hash obj))
((4) (string-hash obj))
((5) (symbol-hash obj))
((6) (number-hash obj))
((8) ((make-vector-hash (make-default-comparator)
bytevector? bytevector-length bytevector-u8-ref) obj))
; Add more here
(else (comparator-hash (registered-comparator (object-type obj)) obj))))
(define (default-ordering a b)
(let ((a-type (object-type a))
(b-type (object-type b)))
(cond
((< a-type b-type) #t)
((> a-type b-type) #f)
(else (dispatch-ordering a-type a b)))))
(define (default-equality a b)
(let ((a-type (object-type a))
(b-type (object-type b)))
(if (= a-type b-type) (dispatch-equality a-type a b) #f)))
(define (make-default-comparator)
(make-comparator
(lambda (obj) #t)
default-equality
default-ordering
default-hash))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a128/comparators.sls 0000664 0000000 0000000 00000002017 14737542645 0022361 0 ustar 00root root 0000000 0000000 (library (srfi :128 comparators)
(export comparator? comparator-ordered? comparator-hashable? make-comparator
make-pair-comparator make-list-comparator make-vector-comparator
make-eq-comparator make-eqv-comparator make-equal-comparator
boolean-hash char-hash char-ci-hash string-hash string-ci-hash
symbol-hash number-hash make-default-comparator default-hash
comparator-register-default! comparator-type-test-predicate
comparator-equality-predicate comparator-ordering-predicate
comparator-hash-function comparator-test-type comparator-check-type
comparator-hash hash-bound hash-salt =? >? <=? >=?
comparator-if<=>)
(import (except (rnrs) define-record-type)
(srfi :99)
(srfi :39)
(only (rnrs r5rs) modulo)
(srfi private include))
(define (exact-integer? x) (and (integer? x) (exact? x)))
(include/resolve ("srfi" "%3a128") "128.body1.scm")
(include/resolve ("srfi" "%3a128") "128.body2.scm"))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a129.sls 0000664 0000000 0000000 00000000163 14737542645 0020030 0 ustar 00root root 0000000 0000000 (library (srfi :129)
(export char-title-case? char-titlecase string-titlecase)
(import (srfi :129 titlecase)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a129/ 0000775 0000000 0000000 00000000000 14737542645 0017305 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a129/titlecase-impl.scm 0000664 0000000 0000000 00000004005 14737542645 0022724 0 ustar 00root root 0000000 0000000 ;;;; Implementation of SRFI 129 titlecase functions
;; Returns #t if argument is a titlecase character, #f if not
(define (char-title-case? ch)
(let* ((codepoint (char->integer ch))
(result (assq codepoint titlecase-chars)))
(if result #t #f)))
;; Returns the single-character titlecase mapping of argument
(define (char-titlecase ch)
(let* ((codepoint (char->integer ch))
(result (assq codepoint title-single-map)))
(if result
(integer->char (cadr result))
(char-upcase ch))))
;; Returns #t if a character is caseless, otherwise #f
(define (char-caseless? ch)
(not (or (char-lower-case? ch) (char-upper-case? ch) (char-title-case? ch))))
;; Push a list onto another list in reverse order
(define (reverse-push new old)
(if (null? new)
old
(reverse-push (cdr new) (cons (car new) old))))
;; Returns the string titlecase mapping of argument
(define (string-titlecase str)
(let loop ((n 0) (result '()))
(if (= n (string-length str))
(apply string (map integer->char (reverse result)))
(let* ((ch (string-ref str n)) (codepoint (char->integer ch)))
(if (or (= n 0) (char-caseless? (string-ref str (- n 1))))
; ch must be titlecased
(let ((multi-title (assq codepoint title-multiple-map)))
(if multi-title
; ch has multiple- or single-character titlecase mapping
(loop (+ n 1) (reverse-push (cdr multi-title) result))
; ch has single-character uppercase mapping
(loop (+ n 1) (reverse-push (list (char->integer (char-upcase ch))) result))))
; ch must be lowercased
(let ((multi-downcase (assq codepoint lower-multiple-map)))
(if multi-downcase
; ch has multiple-character lowercase mapping
(loop (+ n 1) (reverse-push (cdr multi-downcase) result))
; ch has single-character lowercase mapping
(loop (+ n 1) (reverse-push (list (char->integer (char-downcase ch))) result)))))))))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a129/titlecase.sls 0000664 0000000 0000000 00000000472 14737542645 0022010 0 ustar 00root root 0000000 0000000 (library (srfi :129 titlecase)
(export char-title-case? char-titlecase string-titlecase)
(import (except (rnrs) char-titlecase char-title-case? string-titlecase)
(srfi private include))
(include/resolve ("srfi" "%3a129") "titlemaps.scm")
(include/resolve ("srfi" "%3a129") "titlecase-impl.scm"))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a129/titlemaps.scm 0000664 0000000 0000000 00000016254 14737542645 0022023 0 ustar 00root root 0000000 0000000 ;;;; Alists for titlecase functions
;;; Assumes that char->integer and integer->char are a subset of Unicode
;;; codepoint mappings rather than some random codes, as R5RS allows
;;; but R[67]RS do not. It may be necessary to remove some lines if
;;; the codepoints referred to don't correspond to characters present
;;; in the implementation.
;;; These maps are valid from Unicode 5.0 to at least Unicode 8.0
;;; and are expected to be stable for the foreseeable future.
;; Alist mapping titlecase characters to themselves
(define titlecase-chars '(
(#x01C5 #x01C5) ; LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON
(#x01C8 #x01C8) ; LATIN CAPITAL LETTER L WITH SMALL LETTER J
(#x01CB #x01CB) ; LATIN CAPITAL LETTER N WITH SMALL LETTER J
(#x01F2 #x01F2) ; LATIN CAPITAL LETTER D WITH SMALL LETTER Z
(#x1F88 #x1F88) ; GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI
(#x1F89 #x1F89) ; GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI
(#x1F8A #x1F8A) ; GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI
(#x1F8B #x1F8B) ; GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI
(#x1F8C #x1F8C) ; GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI
(#x1F8D #x1F8D) ; GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI
(#x1F8E #x1F8E) ; GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
(#x1F8F #x1F8F) ; GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
(#x1F98 #x1F98) ; GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI
(#x1F99 #x1F99) ; GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI
(#x1F9A #x1F9A) ; GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI
(#x1F9B #x1F9B) ; GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI
(#x1F9C #x1F9C) ; GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI
(#x1F9D #x1F9D) ; GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI
(#x1F9E #x1F9E) ; GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
(#x1F9F #x1F9F) ; GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
(#x1FA8 #x1FA8) ; GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI
(#x1FA9 #x1FA9) ; GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI
(#x1FAA #x1FAA) ; GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI
(#x1FAB #x1FAB) ; GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI
(#x1FAC #x1FAC) ; GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI
(#x1FAD #x1FAD) ; GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI
(#x1FAE #x1FAE) ; GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
(#x1FAF #x1FAF) ; GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
(#x1FBC #x1FBC) ; GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
(#x1FCC #x1FCC) ; GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
(#x1FFC #x1FFC) ; GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
))
;; Alist mapping characters to their single-letter titlecase equivalents
;; when those are distinct from their uppercase equivalents
(define title-single-map (append titlecase-chars '(
(#x01C4 #x01C5) ; LATIN CAPITAL LETTER DZ WITH CARON
(#x01C6 #x01C5) ; LATIN SMALL LETTER DZ WITH CARON
(#x01C7 #x01C8) ; LATIN CAPITAL LETTER LJ
(#x01C9 #x01C8) ; LATIN SMALL LETTER LJ
(#x01CA #x01CB) ; LATIN CAPITAL LETTER NJ
(#x01CC #x01CB) ; LATIN SMALL LETTER NJ
(#x01F1 #x01F2) ; LATIN CAPITAL LETTER DZ
(#x01F3 #x01F2) ; LATIN SMALL LETTER DZ
)))
;; Alist mapping characters to their multiple-letter titlecase equivalents
(define title-multiple-map (append title-single-map '(
(#x00DF #x0053 #x0073) ; LATIN SMALL LETTER SHARP S
(#xFB00 #x0046 #x0066) ; LATIN SMALL LIGATURE FF
(#xFB01 #x0046 #x0069) ; LATIN SMALL LIGATURE FI
(#xFB02 #x0046 #x006C) ; LATIN SMALL LIGATURE FL
(#xFB03 #x0046 #x0066 #x0069) ; LATIN SMALL LIGATURE FFI
(#xFB04 #x0046 #x0066 #x006C) ; LATIN SMALL LIGATURE FFL
(#xFB05 #x0053 #x0074) ; LATIN SMALL LIGATURE LONG S T
(#xFB06 #x0053 #x0074) ; LATIN SMALL LIGATURE ST
(#x0587 #x0535 #x0582) ; ARMENIAN SMALL LIGATURE ECH YIWN
(#xFB13 #x0544 #x0576) ; ARMENIAN SMALL LIGATURE MEN NOW
(#xFB14 #x0544 #x0565) ; ARMENIAN SMALL LIGATURE MEN ECH
(#xFB15 #x0544 #x056B) ; ARMENIAN SMALL LIGATURE MEN INI
(#xFB16 #x054E #x0576) ; ARMENIAN SMALL LIGATURE VEW NOW
(#xFB17 #x0544 #x056D) ; ARMENIAN SMALL LIGATURE MEN XEH
(#x0149 #x02BC #x004E) ; LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
(#x0390 #x0399 #x0308 #x0301) ; GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
(#x03B0 #x03A5 #x0308 #x0301) ; GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
(#x01F0 #x004A #x030C) ; LATIN SMALL LETTER J WITH CARON
(#x1E96 #x0048 #x0331) ; LATIN SMALL LETTER H WITH LINE BELOW
(#x1E97 #x0054 #x0308) ; LATIN SMALL LETTER T WITH DIAERESIS
(#x1E98 #x0057 #x030A) ; LATIN SMALL LETTER W WITH RING ABOVE
(#x1E99 #x0059 #x030A) ; LATIN SMALL LETTER Y WITH RING ABOVE
(#x1E9A #x0041 #x02BE) ; LATIN SMALL LETTER A WITH RIGHT HALF RING
(#x1F50 #x03A5 #x0313) ; GREEK SMALL LETTER UPSILON WITH PSILI
(#x1F52 #x03A5 #x0313 #x0300) ; GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA
(#x1F54 #x03A5 #x0313 #x0301) ; GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA
(#x1F56 #x03A5 #x0313 #x0342) ; GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI
(#x1FB6 #x0391 #x0342) ; GREEK SMALL LETTER ALPHA WITH PERISPOMENI
(#x1FC6 #x0397 #x0342) ; GREEK SMALL LETTER ETA WITH PERISPOMENI
(#x1FD2 #x0399 #x0308 #x0300) ; GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA
(#x1FD3 #x0399 #x0308 #x0301) ; GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
(#x1FD6 #x0399 #x0342) ; GREEK SMALL LETTER IOTA WITH PERISPOMENI
(#x1FD7 #x0399 #x0308 #x0342) ; GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI
(#x1FE2 #x03A5 #x0308 #x0300) ; GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA
(#x1FE3 #x03A5 #x0308 #x0301) ; GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
(#x1FE4 #x03A1 #x0313) ; GREEK SMALL LETTER RHO WITH PSILI
(#x1FE6 #x03A5 #x0342) ; GREEK SMALL LETTER UPSILON WITH PERISPOMENI
(#x1FE7 #x03A5 #x0308 #x0342) ; GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI
(#x1FF6 #x03A9 #x0342) ; GREEK SMALL LETTER OMEGA WITH PERISPOMENI
(#x1FB2 #x1FBA #x0345) ; GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI
(#x1FB4 #x0386 #x0345) ; GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
(#x1FC2 #x1FCA #x0345) ; GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI
(#x1FC4 #x0389 #x0345) ; GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
(#x1FF2 #x1FFA #x0345) ; GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI
(#x1FF4 #x038F #x0345) ; GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
(#x1FB7 #x0391 #x0342 #x0345) ; GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI
(#x1FC7 #x0397 #x0342 #x0345) ; GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI
(#x1FF7 #x03A9 #x0342 #x0345) ; GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI
)))
;; Alist mapping characters to their multiple-character lowercase equivalents
(define lower-multiple-map '(
(#x0130 #x0069 #x0307) ; LATIN CAPITAL LETTER I WITH DOT ABOVE
))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a13.sls 0000664 0000000 0000000 00000003351 14737542645 0017742 0 ustar 00root root 0000000 0000000 #!r6rs
;; Automatically generated by private/make-aliased-libraries.sps
(library (srfi :13)
(export
list->string
make-string
reverse-list->string
string
string->list
string-any
string-append
string-append/shared
string-ci<
string-ci<=
string-ci<>
string-ci=
string-ci>
string-ci>=
string-compare
string-compare-ci
string-concatenate
string-concatenate-reverse
string-concatenate-reverse/shared
string-concatenate/shared
string-contains
string-contains-ci
string-copy
string-copy!
string-count
string-delete
string-downcase
string-downcase!
string-drop
string-drop-right
string-every
string-fill!
string-filter
string-fold
string-fold-right
string-for-each
string-for-each-index
string-hash
string-hash-ci
string-index
string-index-right
string-join
string-length
string-map
string-map!
string-null?
string-pad
string-pad-right
string-prefix-ci?
string-prefix-length
string-prefix-length-ci
string-prefix?
string-ref
string-replace
string-reverse
string-reverse!
string-set!
string-skip
string-skip-right
string-suffix-ci?
string-suffix-length
string-suffix-length-ci
string-suffix?
string-tabulate
string-take
string-take-right
string-titlecase
string-titlecase!
string-tokenize
string-trim
string-trim-both
string-trim-right
string-unfold
string-unfold-right
string-upcase
string-upcase!
string-xcopy!
string<
string<=
string<>
string=
string>
string>=
string?
substring/shared
xsubstring)
(import (srfi :13 strings))
)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a13/ 0000775 0000000 0000000 00000000000 14737542645 0017215 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a13/srfi-13.scm 0000664 0000000 0000000 00000230132 14737542645 0021106 0 ustar 00root root 0000000 0000000 ;;; SRFI 13 string library reference implementation -*- Scheme -*-
;;; Olin Shivers 7/2000
;;;
;;; Copyright (c) 1988-1994 Massachusetts Institute of Technology.
;;; Copyright (c) 1998, 1999, 2000 Olin Shivers. All rights reserved.
;;; The details of the copyrights appear at the end of the file. Short
;;; summary: BSD-style open source.
;;; Exports:
;;; string-map string-map!
;;; string-fold string-unfold
;;; string-fold-right string-unfold-right
;;; string-tabulate string-for-each string-for-each-index
;;; string-every string-any
;;; string-hash string-hash-ci
;;; string-compare string-compare-ci
;;; string= string< string> string<= string>= string<>
;;; string-ci= string-ci< string-ci> string-ci<= string-ci>= string-ci<>
;;; string-downcase string-upcase string-titlecase
;;; string-downcase! string-upcase! string-titlecase!
;;; string-take string-take-right
;;; string-drop string-drop-right
;;; string-pad string-pad-right
;;; string-trim string-trim-right string-trim-both
;;; string-filter string-delete
;;; string-index string-index-right
;;; string-skip string-skip-right
;;; string-count
;;; string-prefix-length string-prefix-length-ci
;;; string-suffix-length string-suffix-length-ci
;;; string-prefix? string-prefix-ci?
;;; string-suffix? string-suffix-ci?
;;; string-contains string-contains-ci
;;; string-copy! substring/shared
;;; string-reverse string-reverse! reverse-list->string
;;; string-concatenate string-concatenate/shared string-concatenate-reverse
;;; string-append/shared
;;; xsubstring string-xcopy!
;;; string-null?
;;; string-join
;;; string-tokenize
;;; string-replace
;;;
;;; R5RS extended:
;;; string->list string-copy string-fill!
;;;
;;; R5RS re-exports:
;;; string? make-string string-length string-ref string-set!
;;;
;;; R5RS re-exports (also defined here but commented-out):
;;; string string-append list->string
;;;
;;; Low-level routines:
;;; make-kmp-restart-vector string-kmp-partial-search kmp-step
;;; string-parse-start+end
;;; string-parse-final-start+end
;;; let-string-start+end
;;; check-substring-spec
;;; substring-spec-ok?
;;; Imports
;;; This is a fairly large library. While it was written for portability, you
;;; must be aware of its dependencies in order to run it in a given scheme
;;; implementation. Here is a complete list of the dependencies it has and the
;;; assumptions it makes beyond stock R5RS Scheme:
;;;
;;; This code has the following non-R5RS dependencies:
;;; - (RECEIVE (var ...) mv-exp body ...) multiple-value binding macro;
;;;
;;; - Various imports from the char-set library for the routines that can
;;; take char-set arguments;
;;;
;;; - An n-ary ERROR procedure;
;;;
;;; - BITWISE-AND for the hash functions;
;;;
;;; - A simple CHECK-ARG procedure for checking parameter values; it is
;;; (lambda (pred val proc)
;;; (if (pred val) val (error "Bad arg" val pred proc)))
;;;
;;; - :OPTIONAL and LET-OPTIONALS* macros for parsing, defaulting &
;;; type-checking optional parameters from a rest argument;
;;;
;;; - CHAR-CASED? and CHAR-TITLECASE for the STRING-TITLECASE &
;;; STRING-TITLECASE! procedures. The former returns true iff a character is
;;; one that has case distinctions; in ASCII it returns true on a-z and A-Z.
;;; CHAR-TITLECASE is analagous to CHAR-UPCASE and CHAR-DOWNCASE. In ASCII &
;;; Latin-1, it is the same as CHAR-UPCASE.
;;;
;;; The code depends upon a small set of core string primitives from R5RS:
;;; MAKE-STRING STRING-REF STRING-SET! STRING? STRING-LENGTH SUBSTRING
;;; (Actually, SUBSTRING is not a primitive, but we assume that an
;;; implementation's native version is probably faster than one we could
;;; define, so we import it from R5RS.)
;;;
;;; The code depends upon a small set of R5RS character primitives:
;;; char? char=? char-ci=? char char-ci
;;; char-upcase char-downcase
;;; char->integer (for the hash functions)
;;;
;;; We assume the following:
;;; - CHAR-DOWNCASE o CHAR-UPCASE = CHAR-DOWNCASE
;;; - CHAR-CI=? is equivalent to
;;; (lambda (c1 c2) (char=? (char-downcase (char-upcase c1))
;;; (char-downcase (char-upcase c2))))
;;; - CHAR-UPCASE, CHAR-DOWNCASE and CHAR-TITLECASE are locale-insensitive
;;; and consistent with Unicode's 1-1 char-mapping spec.
;;; These things are typically true, but if not, you would need to modify
;;; the case-mapping and case-insensitive routines.
;;; Enough introductory blather. On to the source code. (But see the end of
;;; the file for further notes on porting & performance tuning.)
;;; Support for START/END substring specs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This macro parses optional start/end arguments from arg lists, defaulting
;;; them to 0/(string-length s), and checks them for correctness.
(define-syntax let-string-start+end
(syntax-rules ()
((let-string-start+end (start end) proc s-exp args-exp body ...)
(receive (start end) (string-parse-final-start+end proc s-exp args-exp)
body ...))
((let-string-start+end (start end rest) proc s-exp args-exp body ...)
(receive (rest start end) (string-parse-start+end proc s-exp args-exp)
body ...))))
;;; This one parses out a *pair* of final start/end indices.
;;; Not exported; for internal use.
(define-syntax let-string-start+end2
(syntax-rules ()
((l-s-s+e2 (start1 end1 start2 end2) proc s1 s2 args body ...)
(let ((procv proc)) ; Make sure PROC is only evaluated once.
(let-string-start+end (start1 end1 rest) procv s1 args
(let-string-start+end (start2 end2) procv s2 rest
body ...))))))
;;; Returns three values: rest start end
(define (string-parse-start+end proc s args)
(if (not (string? s)) (error "Non-string value" proc s))
(let ((slen (string-length s)))
(if (pair? args)
(let ((start (car args))
(args (cdr args)))
(if (and (integer? start) (exact? start) (>= start 0))
(receive (end args)
(if (pair? args)
(let ((end (car args))
(args (cdr args)))
(if (and (integer? end) (exact? end) (<= end slen))
(values end args)
(error "Illegal substring END spec" proc end s)))
(values slen args))
(if (<= start end) (values args start end)
(error "Illegal substring START/END spec"
proc start end s)))
(error "Illegal substring START spec" proc start s)))
(values '() 0 slen))))
(define (string-parse-final-start+end proc s args)
(receive (rest start end) (string-parse-start+end proc s args)
(if (pair? rest) (error "Extra arguments to procedure" proc rest)
(values start end))))
(define (substring-spec-ok? s start end)
(and (string? s)
(integer? start)
(exact? start)
(integer? end)
(exact? end)
(<= 0 start)
(<= start end)
(<= end (string-length s))))
(define (check-substring-spec proc s start end)
(if (not (substring-spec-ok? s start end))
(error "Illegal substring spec." proc s start end)))
;;; Defined by R5RS, so commented out here.
;(define (string . chars)
; (let* ((len (length chars))
; (ans (make-string len)))
; (do ((i 0 (+ i 1))
; (chars chars (cdr chars)))
; ((>= i len))
; (string-set! ans i (car chars)))
; ans))
;
;(define (string . chars) (string-unfold null? car cdr chars))
;;; substring/shared S START [END]
;;; string-copy S [START END]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; All this goop is just arg parsing & checking surrounding a call to the
;;; actual primitive, %SUBSTRING/SHARED.
(define (substring/shared s start . maybe-end)
(check-arg string? s substring/shared)
(let ((slen (string-length s)))
(check-arg (lambda (start) (and (integer? start) (exact? start) (<= 0 start)))
start substring/shared)
(%substring/shared s start
(:optional maybe-end slen
(lambda (end) (and (integer? end)
(exact? end)
(<= start end)
(<= end slen)))))))
;;; Split out so that other routines in this library can avoid arg-parsing
;;; overhead for END parameter.
(define (%substring/shared s start end)
(if (and (zero? start) (= end (string-length s))) s
(substring s start end)))
(define (string-copy s . maybe-start+end)
(let-string-start+end (start end) string-copy s maybe-start+end
(substring s start end)))
;This library uses the R5RS SUBSTRING, but doesn't export it.
;Here is a definition, just for completeness.
;(define (substring s start end)
; (check-substring-spec substring s start end)
; (let* ((slen (- end start))
; (ans (make-string slen)))
; (do ((i 0 (+ i 1))
; (j start (+ j 1)))
; ((>= i slen) ans)
; (string-set! ans i (string-ref s j)))))
;;; Basic iterators and other higher-order abstractions
;;; (string-map proc s [start end])
;;; (string-map! proc s [start end])
;;; (string-fold kons knil s [start end])
;;; (string-fold-right kons knil s [start end])
;;; (string-unfold p f g seed [base make-final])
;;; (string-unfold-right p f g seed [base make-final])
;;; (string-for-each proc s [start end])
;;; (string-for-each-index proc s [start end])
;;; (string-every char-set/char/pred s [start end])
;;; (string-any char-set/char/pred s [start end])
;;; (string-tabulate proc len)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; You want compiler support for high-level transforms on fold and unfold ops.
;;; You'd at least like a lot of inlining for clients of these procedures.
;;; Don't hold your breath.
(define (string-map proc s . maybe-start+end)
(check-arg procedure? proc string-map)
(let-string-start+end (start end) string-map s maybe-start+end
(%string-map proc s start end)))
(define (%string-map proc s start end) ; Internal utility
(let* ((len (- end start))
(ans (make-string len)))
(do ((i (- end 1) (- i 1))
(j (- len 1) (- j 1)))
((< j 0))
(string-set! ans j (proc (string-ref s i))))
ans))
(define (string-map! proc s . maybe-start+end)
(check-arg procedure? proc string-map!)
(let-string-start+end (start end) string-map! s maybe-start+end
(%string-map! proc s start end)))
(define (%string-map! proc s start end)
(do ((i (- end 1) (- i 1)))
((< i start))
(string-set! s i (proc (string-ref s i)))))
(define (string-fold kons knil s . maybe-start+end)
(check-arg procedure? kons string-fold)
(let-string-start+end (start end) string-fold s maybe-start+end
(let lp ((v knil) (i start))
(if (< i end) (lp (kons (string-ref s i) v) (+ i 1))
v))))
(define (string-fold-right kons knil s . maybe-start+end)
(check-arg procedure? kons string-fold-right)
(let-string-start+end (start end) string-fold-right s maybe-start+end
(let lp ((v knil) (i (- end 1)))
(if (>= i start) (lp (kons (string-ref s i) v) (- i 1))
v))))
;;; (string-unfold p f g seed [base make-final])
;;; This is the fundamental constructor for strings.
;;; - G is used to generate a series of "seed" values from the initial seed:
;;; SEED, (G SEED), (G^2 SEED), (G^3 SEED), ...
;;; - P tells us when to stop -- when it returns true when applied to one
;;; of these seed values.
;;; - F maps each seed value to the corresponding character
;;; in the result string. These chars are assembled into the
;;; string in a left-to-right order.
;;; - BASE is the optional initial/leftmost portion of the constructed string;
;;; it defaults to the empty string "".
;;; - MAKE-FINAL is applied to the terminal seed value (on which P returns
;;; true) to produce the final/rightmost portion of the constructed string.
;;; It defaults to (LAMBDA (X) "").
;;;
;;; In other words, the following (simple, inefficient) definition holds:
;;; (define (string-unfold p f g seed base make-final)
;;; (string-append base
;;; (let recur ((seed seed))
;;; (if (p seed) (make-final seed)
;;; (string-append (string (f seed))
;;; (recur (g seed)))))))
;;;
;;; STRING-UNFOLD is a fairly powerful constructor -- you can use it to
;;; reverse a string, copy a string, convert a list to a string, read
;;; a port into a string, and so forth. Examples:
;;; (port->string port) =
;;; (string-unfold (compose eof-object? peek-char)
;;; read-char values port)
;;;
;;; (list->string lis) = (string-unfold null? car cdr lis)
;;;
;;; (tabulate-string f size) = (string-unfold (lambda (i) (= i size)) f add1 0)
;;; A problem with the following simple formulation is that it pushes one
;;; stack frame for every char in the result string -- an issue if you are
;;; using it to read a 100kchar string. So we don't use it -- but I include
;;; it to give a clear, straightforward description of what the function
;;; does.
;(define (string-unfold p f g seed base make-final)
; (let ((ans (let recur ((seed seed) (i (string-length base)))
; (if (p seed)
; (let* ((final (make-final seed))
; (ans (make-string (+ i (string-length final)))))
; (string-copy! ans i final)
; ans)
;
; (let* ((c (f seed))
; (s (recur (g seed) (+ i 1))))
; (string-set! s i c)
; s)))))
; (string-copy! ans 0 base)
; ans))
;;; The strategy is to allocate a series of chunks into which we stash the
;;; chars as we generate them. Chunk size goes up in powers of two starting
;;; with 40 and levelling out at 4k, i.e.
;;; 40 40 80 160 320 640 1280 2560 4096 4096 4096 4096 4096...
;;; This should work pretty well for short strings, 1-line (80 char) strings,
;;; and longer ones. When done, we allocate an answer string and copy the
;;; chars over from the chunk buffers.
(define (string-unfold p f g seed . base+make-final)
(check-arg procedure? p string-unfold)
(check-arg procedure? f string-unfold)
(check-arg procedure? g string-unfold)
(let-optionals* base+make-final
((base "" (string? base))
(make-final (lambda (x) "") (procedure? make-final)))
(let lp ((chunks '()) ; Previously filled chunks
(nchars 0) ; Number of chars in CHUNKS
(chunk (make-string 40)) ; Current chunk into which we write
(chunk-len 40)
(i 0) ; Number of chars written into CHUNK
(seed seed))
(let lp2 ((i i) (seed seed))
(if (not (p seed))
(let ((c (f seed))
(seed (g seed)))
(if (< i chunk-len)
(begin (string-set! chunk i c)
(lp2 (+ i 1) seed))
(let* ((nchars2 (+ chunk-len nchars))
(chunk-len2 (min 4096 nchars2))
(new-chunk (make-string chunk-len2)))
(string-set! new-chunk 0 c)
(lp (cons chunk chunks) (+ nchars chunk-len)
new-chunk chunk-len2 1 seed))))
;; We're done. Make the answer string & install the bits.
(let* ((final (make-final seed))
(flen (string-length final))
(base-len (string-length base))
(j (+ base-len nchars i))
(ans (make-string (+ j flen))))
(%string-copy! ans j final 0 flen) ; Install FINAL.
(let ((j (- j i)))
(%string-copy! ans j chunk 0 i) ; Install CHUNK[0,I).
(let lp ((j j) (chunks chunks)) ; Install CHUNKS.
(if (pair? chunks)
(let* ((chunk (car chunks))
(chunks (cdr chunks))
(chunk-len (string-length chunk))
(j (- j chunk-len)))
(%string-copy! ans j chunk 0 chunk-len)
(lp j chunks)))))
(%string-copy! ans 0 base 0 base-len) ; Install BASE.
ans))))))
(define (string-unfold-right p f g seed . base+make-final)
(let-optionals* base+make-final
((base "" (string? base))
(make-final (lambda (x) "") (procedure? make-final)))
(let lp ((chunks '()) ; Previously filled chunks
(nchars 0) ; Number of chars in CHUNKS
(chunk (make-string 40)) ; Current chunk into which we write
(chunk-len 40)
(i 40) ; Number of chars available in CHUNK
(seed seed))
(let lp2 ((i i) (seed seed)) ; Fill up CHUNK from right
(if (not (p seed)) ; to left.
(let ((c (f seed))
(seed (g seed)))
(if (> i 0)
(let ((i (- i 1)))
(string-set! chunk i c)
(lp2 i seed))
(let* ((nchars2 (+ chunk-len nchars))
(chunk-len2 (min 4096 nchars2))
(new-chunk (make-string chunk-len2))
(i (- chunk-len2 1)))
(string-set! new-chunk i c)
(lp (cons chunk chunks) (+ nchars chunk-len)
new-chunk chunk-len2 i seed))))
;; We're done. Make the answer string & install the bits.
(let* ((final (make-final seed))
(flen (string-length final))
(base-len (string-length base))
(chunk-used (- chunk-len i))
(j (+ base-len nchars chunk-used))
(ans (make-string (+ j flen))))
(%string-copy! ans 0 final 0 flen) ; Install FINAL.
(%string-copy! ans flen chunk i chunk-len); Install CHUNK[I,).
(let lp ((j (+ flen chunk-used)) ; Install CHUNKS.
(chunks chunks))
(if (pair? chunks)
(let* ((chunk (car chunks))
(chunks (cdr chunks))
(chunk-len (string-length chunk)))
(%string-copy! ans j chunk 0 chunk-len)
(lp (+ j chunk-len) chunks))
(%string-copy! ans j base 0 base-len))); Install BASE.
ans))))))
(define (string-for-each proc s . maybe-start+end)
(check-arg procedure? proc string-for-each)
(let-string-start+end (start end) string-for-each s maybe-start+end
(let lp ((i start))
(if (< i end)
(begin (proc (string-ref s i))
(lp (+ i 1)))))))
(define (string-for-each-index proc s . maybe-start+end)
(check-arg procedure? proc string-for-each-index)
(let-string-start+end (start end) string-for-each-index s maybe-start+end
(let lp ((i start))
(if (< i end) (begin (proc i) (lp (+ i 1)))))))
(define (string-every criterion s . maybe-start+end)
(let-string-start+end (start end) string-every s maybe-start+end
(cond ((char? criterion)
(let lp ((i start))
(or (>= i end)
(and (char=? criterion (string-ref s i))
(lp (+ i 1))))))
((char-set? criterion)
(let lp ((i start))
(or (>= i end)
(and (char-set-contains? criterion (string-ref s i))
(lp (+ i 1))))))
((procedure? criterion) ; Slightly funky loop so that
(or (= start end) ; final (PRED S[END-1]) call
(let lp ((i start)) ; is a tail call.
(let ((c (string-ref s i))
(i1 (+ i 1)))
(if (= i1 end) (criterion c) ; Tail call.
(and (criterion c) (lp i1)))))))
(else (error "Second param is neither char-set, char, or predicate procedure."
string-every criterion)))))
(define (string-any criterion s . maybe-start+end)
(let-string-start+end (start end) string-any s maybe-start+end
(cond ((char? criterion)
(let lp ((i start))
(and (< i end)
(or (char=? criterion (string-ref s i))
(lp (+ i 1))))))
((char-set? criterion)
(let lp ((i start))
(and (< i end)
(or (char-set-contains? criterion (string-ref s i))
(lp (+ i 1))))))
((procedure? criterion) ; Slightly funky loop so that
(and (< start end) ; final (PRED S[END-1]) call
(let lp ((i start)) ; is a tail call.
(let ((c (string-ref s i))
(i1 (+ i 1)))
(if (= i1 end) (criterion c) ; Tail call
(or (criterion c) (lp i1)))))))
(else (error "Second param is neither char-set, char, or predicate procedure."
string-any criterion)))))
(define (string-tabulate proc len)
(check-arg procedure? proc string-tabulate)
(check-arg (lambda (val) (and (integer? val) (exact? val) (<= 0 val)))
len string-tabulate)
(let ((s (make-string len)))
(do ((i (- len 1) (- i 1)))
((< i 0))
(string-set! s i (proc i)))
s))
;;; string-prefix-length[-ci] s1 s2 [start1 end1 start2 end2]
;;; string-suffix-length[-ci] s1 s2 [start1 end1 start2 end2]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Find the length of the common prefix/suffix.
;;; It is not required that the two substrings passed be of equal length.
;;; This was microcode in MIT Scheme -- a very tightly bummed primitive.
;;; %STRING-PREFIX-LENGTH is the core routine of all string-comparisons,
;;; so should be as tense as possible.
(define (%string-prefix-length s1 start1 end1 s2 start2 end2)
(let* ((delta (min (- end1 start1) (- end2 start2)))
(end1 (+ start1 delta)))
(if (and (eq? s1 s2) (= start1 start2)) ; EQ fast path
delta
(let lp ((i start1) (j start2)) ; Regular path
(if (or (>= i end1)
(not (char=? (string-ref s1 i)
(string-ref s2 j))))
(- i start1)
(lp (+ i 1) (+ j 1)))))))
(define (%string-suffix-length s1 start1 end1 s2 start2 end2)
(let* ((delta (min (- end1 start1) (- end2 start2)))
(start1 (- end1 delta)))
(if (and (eq? s1 s2) (= end1 end2)) ; EQ fast path
delta
(let lp ((i (- end1 1)) (j (- end2 1))) ; Regular path
(if (or (< i start1)
(not (char=? (string-ref s1 i)
(string-ref s2 j))))
(- (- end1 i) 1)
(lp (- i 1) (- j 1)))))))
(define (%string-prefix-length-ci s1 start1 end1 s2 start2 end2)
(let* ((delta (min (- end1 start1) (- end2 start2)))
(end1 (+ start1 delta)))
(if (and (eq? s1 s2) (= start1 start2)) ; EQ fast path
delta
(let lp ((i start1) (j start2)) ; Regular path
(if (or (>= i end1)
(not (char-ci=? (string-ref s1 i)
(string-ref s2 j))))
(- i start1)
(lp (+ i 1) (+ j 1)))))))
(define (%string-suffix-length-ci s1 start1 end1 s2 start2 end2)
(let* ((delta (min (- end1 start1) (- end2 start2)))
(start1 (- end1 delta)))
(if (and (eq? s1 s2) (= end1 end2)) ; EQ fast path
delta
(let lp ((i (- end1 1)) (j (- end2 1))) ; Regular path
(if (or (< i start1)
(not (char-ci=? (string-ref s1 i)
(string-ref s2 j))))
(- (- end1 i) 1)
(lp (- i 1) (- j 1)))))))
(define (string-prefix-length s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-prefix-length s1 s2 maybe-starts+ends
(%string-prefix-length s1 start1 end1 s2 start2 end2)))
(define (string-suffix-length s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-suffix-length s1 s2 maybe-starts+ends
(%string-suffix-length s1 start1 end1 s2 start2 end2)))
(define (string-prefix-length-ci s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-prefix-length-ci s1 s2 maybe-starts+ends
(%string-prefix-length-ci s1 start1 end1 s2 start2 end2)))
(define (string-suffix-length-ci s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-suffix-length-ci s1 s2 maybe-starts+ends
(%string-suffix-length-ci s1 start1 end1 s2 start2 end2)))
;;; string-prefix? s1 s2 [start1 end1 start2 end2]
;;; string-suffix? s1 s2 [start1 end1 start2 end2]
;;; string-prefix-ci? s1 s2 [start1 end1 start2 end2]
;;; string-suffix-ci? s1 s2 [start1 end1 start2 end2]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; These are all simple derivatives of the previous counting funs.
(define (string-prefix? s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-prefix? s1 s2 maybe-starts+ends
(%string-prefix? s1 start1 end1 s2 start2 end2)))
(define (string-suffix? s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-suffix? s1 s2 maybe-starts+ends
(%string-suffix? s1 start1 end1 s2 start2 end2)))
(define (string-prefix-ci? s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-prefix-ci? s1 s2 maybe-starts+ends
(%string-prefix-ci? s1 start1 end1 s2 start2 end2)))
(define (string-suffix-ci? s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-suffix-ci? s1 s2 maybe-starts+ends
(%string-suffix-ci? s1 start1 end1 s2 start2 end2)))
;;; Here are the internal routines that do the real work.
(define (%string-prefix? s1 start1 end1 s2 start2 end2)
(let ((len1 (- end1 start1)))
(and (<= len1 (- end2 start2)) ; Quick check
(= (%string-prefix-length s1 start1 end1
s2 start2 end2)
len1))))
(define (%string-suffix? s1 start1 end1 s2 start2 end2)
(let ((len1 (- end1 start1)))
(and (<= len1 (- end2 start2)) ; Quick check
(= len1 (%string-suffix-length s1 start1 end1
s2 start2 end2)))))
(define (%string-prefix-ci? s1 start1 end1 s2 start2 end2)
(let ((len1 (- end1 start1)))
(and (<= len1 (- end2 start2)) ; Quick check
(= len1 (%string-prefix-length-ci s1 start1 end1
s2 start2 end2)))))
(define (%string-suffix-ci? s1 start1 end1 s2 start2 end2)
(let ((len1 (- end1 start1)))
(and (<= len1 (- end2 start2)) ; Quick check
(= len1 (%string-suffix-length-ci s1 start1 end1
s2 start2 end2)))))
;;; string-compare s1 s2 proc< proc= proc> [start1 end1 start2 end2]
;;; string-compare-ci s1 s2 proc< proc= proc> [start1 end1 start2 end2]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Primitive string-comparison functions.
;;; Continuation order is different from MIT Scheme.
;;; Continuations are applied to s1's mismatch index;
;;; in the case of equality, this is END1.
(define (%string-compare s1 start1 end1 s2 start2 end2
proc< proc= proc>)
(let ((size1 (- end1 start1))
(size2 (- end2 start2)))
(let ((match (%string-prefix-length s1 start1 end1 s2 start2 end2)))
(if (= match size1)
((if (= match size2) proc= proc<) end1)
((if (= match size2)
proc>
(if (char (string-ref s1 (+ start1 match))
(string-ref s2 (+ start2 match)))
proc< proc>))
(+ match start1))))))
(define (%string-compare-ci s1 start1 end1 s2 start2 end2
proc< proc= proc>)
(let ((size1 (- end1 start1))
(size2 (- end2 start2)))
(let ((match (%string-prefix-length-ci s1 start1 end1 s2 start2 end2)))
(if (= match size1)
((if (= match size2) proc= proc<) end1)
((if (= match size2) proc>
(if (char-ci (string-ref s1 (+ start1 match))
(string-ref s2 (+ start2 match)))
proc< proc>))
(+ start1 match))))))
(define (string-compare s1 s2 proc< proc= proc> . maybe-starts+ends)
(check-arg procedure? proc< string-compare)
(check-arg procedure? proc= string-compare)
(check-arg procedure? proc> string-compare)
(let-string-start+end2 (start1 end1 start2 end2)
string-compare s1 s2 maybe-starts+ends
(%string-compare s1 start1 end1 s2 start2 end2 proc< proc= proc>)))
(define (string-compare-ci s1 s2 proc< proc= proc> . maybe-starts+ends)
(check-arg procedure? proc< string-compare-ci)
(check-arg procedure? proc= string-compare-ci)
(check-arg procedure? proc> string-compare-ci)
(let-string-start+end2 (start1 end1 start2 end2)
string-compare-ci s1 s2 maybe-starts+ends
(%string-compare-ci s1 start1 end1 s2 start2 end2 proc< proc= proc>)))
;;; string= string<> string-ci= string-ci<>
;;; string< string> string-ci< string-ci>
;;; string<= string>= string-ci<= string-ci>=
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Simple definitions in terms of the previous comparison funs.
;;; I sure hope the %STRING-COMPARE calls get integrated.
(define (string= s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string= s1 s2 maybe-starts+ends
(and (= (- end1 start1) (- end2 start2)) ; Quick filter
(or (and (eq? s1 s2) (= start1 start2)) ; Fast path
(%string-compare s1 start1 end1 s2 start2 end2 ; Real test
(lambda (i) #f)
values
(lambda (i) #f))))))
(define (string<> s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string<> s1 s2 maybe-starts+ends
(or (not (= (- end1 start1) (- end2 start2))) ; Fast path
(and (not (and (eq? s1 s2) (= start1 start2))) ; Quick filter
(%string-compare s1 start1 end1 s2 start2 end2 ; Real test
values
(lambda (i) #f)
values)))))
(define (string< s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string< s1 s2 maybe-starts+ends
(if (and (eq? s1 s2) (= start1 start2)) ; Fast path
(< end1 end2)
(%string-compare s1 start1 end1 s2 start2 end2 ; Real test
values
(lambda (i) #f)
(lambda (i) #f)))))
(define (string> s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string> s1 s2 maybe-starts+ends
(if (and (eq? s1 s2) (= start1 start2)) ; Fast path
(> end1 end2)
(%string-compare s1 start1 end1 s2 start2 end2 ; Real test
(lambda (i) #f)
(lambda (i) #f)
values))))
(define (string<= s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string<= s1 s2 maybe-starts+ends
(if (and (eq? s1 s2) (= start1 start2)) ; Fast path
(<= end1 end2)
(%string-compare s1 start1 end1 s2 start2 end2 ; Real test
values
values
(lambda (i) #f)))))
(define (string>= s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string>= s1 s2 maybe-starts+ends
(if (and (eq? s1 s2) (= start1 start2)) ; Fast path
(>= end1 end2)
(%string-compare s1 start1 end1 s2 start2 end2 ; Real test
(lambda (i) #f)
values
values))))
(define (string-ci= s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-ci= s1 s2 maybe-starts+ends
(and (= (- end1 start1) (- end2 start2)) ; Quick filter
(or (and (eq? s1 s2) (= start1 start2)) ; Fast path
(%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
(lambda (i) #f)
values
(lambda (i) #f))))))
(define (string-ci<> s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-ci<> s1 s2 maybe-starts+ends
(or (not (= (- end1 start1) (- end2 start2))) ; Fast path
(and (not (and (eq? s1 s2) (= start1 start2))) ; Quick filter
(%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
values
(lambda (i) #f)
values)))))
(define (string-ci< s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-ci< s1 s2 maybe-starts+ends
(if (and (eq? s1 s2) (= start1 start2)) ; Fast path
(< end1 end2)
(%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
values
(lambda (i) #f)
(lambda (i) #f)))))
(define (string-ci> s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-ci> s1 s2 maybe-starts+ends
(if (and (eq? s1 s2) (= start1 start2)) ; Fast path
(> end1 end2)
(%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
(lambda (i) #f)
(lambda (i) #f)
values))))
(define (string-ci<= s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-ci<= s1 s2 maybe-starts+ends
(if (and (eq? s1 s2) (= start1 start2)) ; Fast path
(<= end1 end2)
(%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
values
values
(lambda (i) #f)))))
(define (string-ci>= s1 s2 . maybe-starts+ends)
(let-string-start+end2 (start1 end1 start2 end2)
string-ci>= s1 s2 maybe-starts+ends
(if (and (eq? s1 s2) (= start1 start2)) ; Fast path
(>= end1 end2)
(%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
(lambda (i) #f)
values
values))))
;;; Hash
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Compute (c + 37 c + 37^2 c + ...) modulo BOUND, with sleaze thrown in
;;; to keep the intermediate values small. (We do the calculation with just
;;; enough bits to represent BOUND, masking off high bits at each step in
;;; calculation. If this screws up any important properties of the hash
;;; function I'd like to hear about it. -Olin)
;;;
;;; If you keep BOUND small enough, the intermediate calculations will
;;; always be fixnums. How small is dependent on the underlying Scheme system;
;;; we use a default BOUND of 2^22 = 4194304, which should hack it in
;;; Schemes that give you at least 29 signed bits for fixnums. The core
;;; calculation that you don't want to overflow is, worst case,
;;; (+ 65535 (* 37 (- bound 1)))
;;; where 65535 is the max character code. Choose the default BOUND to be the
;;; biggest power of two that won't cause this expression to fixnum overflow,
;;; and everything will be copacetic.
(define (%string-hash s char->int bound start end)
(let ((iref (lambda (s i) (char->int (string-ref s i))))
;; Compute a 111...1 mask that will cover BOUND-1:
(mask (let lp ((i #x10000)) ; Let's skip first 16 iterations, eh?
(if (>= i bound) (- i 1) (lp (+ i i))))))
(let lp ((i start) (ans 0))
(if (>= i end) (modulo ans bound)
(lp (+ i 1) (bitwise-and mask (+ (* 37 ans) (iref s i))))))))
(define (string-hash s . maybe-bound+start+end)
(let-optionals* maybe-bound+start+end ((bound 4194304 (and (integer? bound)
(exact? bound)
(<= 0 bound)))
rest)
(let ((bound (if (zero? bound) 4194304 bound))) ; 0 means default.
(let-string-start+end (start end) string-hash s rest
(%string-hash s char->integer bound start end)))))
(define (string-hash-ci s . maybe-bound+start+end)
(let-optionals* maybe-bound+start+end ((bound 4194304 (and (integer? bound)
(exact? bound)
(<= 0 bound)))
rest)
(let ((bound (if (zero? bound) 4194304 bound))) ; 0 means default.
(let-string-start+end (start end) string-hash-ci s rest
(%string-hash s (lambda (c) (char->integer (char-downcase c)))
bound start end)))))
;;; Case hacking
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; string-upcase s [start end]
;;; string-upcase! s [start end]
;;; string-downcase s [start end]
;;; string-downcase! s [start end]
;;;
;;; string-titlecase s [start end]
;;; string-titlecase! s [start end]
;;; Capitalize every contiguous alpha sequence: capitalise
;;; first char, lowercase rest.
(define (string-upcase s . maybe-start+end)
(let-string-start+end (start end) string-upcase s maybe-start+end
(%string-map char-upcase s start end)))
(define (string-upcase! s . maybe-start+end)
(let-string-start+end (start end) string-upcase! s maybe-start+end
(%string-map! char-upcase s start end)))
(define (string-downcase s . maybe-start+end)
(let-string-start+end (start end) string-downcase s maybe-start+end
(%string-map char-downcase s start end)))
(define (string-downcase! s . maybe-start+end)
(let-string-start+end (start end) string-downcase! s maybe-start+end
(%string-map! char-downcase s start end)))
(define (%string-titlecase! s start end)
(let lp ((i start))
(cond ((string-index s char-cased? i end) =>
(lambda (i)
(string-set! s i (char-titlecase (string-ref s i)))
(let ((i1 (+ i 1)))
(cond ((string-skip s char-cased? i1 end) =>
(lambda (j)
(string-downcase! s i1 j)
(lp (+ j 1))))
(else (string-downcase! s i1 end)))))))))
(define (string-titlecase! s . maybe-start+end)
(let-string-start+end (start end) string-titlecase! s maybe-start+end
(%string-titlecase! s start end)))
(define (string-titlecase s . maybe-start+end)
(let-string-start+end (start end) string-titlecase! s maybe-start+end
(let ((ans (substring s start end)))
(%string-titlecase! ans 0 (- end start))
ans)))
;;; Cutting & pasting strings
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; string-take string nchars
;;; string-drop string nchars
;;;
;;; string-take-right string nchars
;;; string-drop-right string nchars
;;;
;;; string-pad string k [char start end]
;;; string-pad-right string k [char start end]
;;;
;;; string-trim string [char/char-set/pred start end]
;;; string-trim-right string [char/char-set/pred start end]
;;; string-trim-both string [char/char-set/pred start end]
;;;
;;; These trimmers invert the char-set meaning from MIT Scheme -- you
;;; say what you want to trim.
(define (string-take s n)
(check-arg string? s string-take)
(check-arg (lambda (val) (and (integer? n) (exact? n)
(<= 0 n (string-length s))))
n string-take)
(%substring/shared s 0 n))
(define (string-take-right s n)
(check-arg string? s string-take-right)
(let ((len (string-length s)))
(check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len)))
n string-take-right)
(%substring/shared s (- len n) len)))
(define (string-drop s n)
(check-arg string? s string-drop)
(let ((len (string-length s)))
(check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len)))
n string-drop)
(%substring/shared s n len)))
(define (string-drop-right s n)
(check-arg string? s string-drop-right)
(let ((len (string-length s)))
(check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len)))
n string-drop-right)
(%substring/shared s 0 (- len n))))
(define (string-trim s . criterion+start+end)
(let-optionals* criterion+start+end ((criterion char-set:whitespace) rest)
(let-string-start+end (start end) string-trim s rest
(cond ((string-skip s criterion start end) =>
(lambda (i) (%substring/shared s i end)))
(else "")))))
(define (string-trim-right s . criterion+start+end)
(let-optionals* criterion+start+end ((criterion char-set:whitespace) rest)
(let-string-start+end (start end) string-trim-right s rest
(cond ((string-skip-right s criterion start end) =>
(lambda (i) (%substring/shared s 0 (+ 1 i))))
(else "")))))
(define (string-trim-both s . criterion+start+end)
(let-optionals* criterion+start+end ((criterion char-set:whitespace) rest)
(let-string-start+end (start end) string-trim-both s rest
(cond ((string-skip s criterion start end) =>
(lambda (i)
(%substring/shared s i (+ 1 (string-skip-right s criterion i end)))))
(else "")))))
(define (string-pad-right s n . char+start+end)
(let-optionals* char+start+end ((char #\space (char? char)) rest)
(let-string-start+end (start end) string-pad-right s rest
(check-arg (lambda (n) (and (integer? n) (exact? n) (<= 0 n)))
n string-pad-right)
(let ((len (- end start)))
(if (<= n len)
(%substring/shared s start (+ start n))
(let ((ans (make-string n char)))
(%string-copy! ans 0 s start end)
ans))))))
(define (string-pad s n . char+start+end)
(let-optionals* char+start+end ((char #\space (char? char)) rest)
(let-string-start+end (start end) string-pad s rest
(check-arg (lambda (n) (and (integer? n) (exact? n) (<= 0 n)))
n string-pad)
(let ((len (- end start)))
(if (<= n len)
(%substring/shared s (- end n) end)
(let ((ans (make-string n char)))
(%string-copy! ans (- n len) s start end)
ans))))))
;;; Filtering strings
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; string-delete char/char-set/pred string [start end]
;;; string-filter char/char-set/pred string [start end]
;;;
;;; If the criterion is a char or char-set, we scan the string twice with
;;; string-fold -- once to determine the length of the result string,
;;; and once to do the filtered copy.
;;; If the criterion is a predicate, we don't do this double-scan strategy,
;;; because the predicate might have side-effects or be very expensive to
;;; compute. So we preallocate a temp buffer pessimistically, and only do
;;; one scan over S. This is likely to be faster and more space-efficient
;;; than consing a list.
(define (string-delete criterion s . maybe-start+end)
(let-string-start+end (start end) string-delete s maybe-start+end
(if (procedure? criterion)
(let* ((slen (- end start))
(temp (make-string slen))
(ans-len (string-fold (lambda (c i)
(if (criterion c) i
(begin (string-set! temp i c)
(+ i 1))))
0 s start end)))
(if (= ans-len slen) temp (substring temp 0 ans-len)))
(let* ((cset (cond ((char-set? criterion) criterion)
((char? criterion) (char-set criterion))
(else (error "string-delete criterion not predicate, char or char-set" criterion))))
(len (string-fold (lambda (c i) (if (char-set-contains? cset c)
i
(+ i 1)))
0 s start end))
(ans (make-string len)))
(string-fold (lambda (c i) (if (char-set-contains? cset c)
i
(begin (string-set! ans i c)
(+ i 1))))
0 s start end)
ans))))
(define (string-filter criterion s . maybe-start+end)
(let-string-start+end (start end) string-filter s maybe-start+end
(if (procedure? criterion)
(let* ((slen (- end start))
(temp (make-string slen))
(ans-len (string-fold (lambda (c i)
(if (criterion c)
(begin (string-set! temp i c)
(+ i 1))
i))
0 s start end)))
(if (= ans-len slen) temp (substring temp 0 ans-len)))
(let* ((cset (cond ((char-set? criterion) criterion)
((char? criterion) (char-set criterion))
(else (error "string-delete criterion not predicate, char or char-set" criterion))))
(len (string-fold (lambda (c i) (if (char-set-contains? cset c)
(+ i 1)
i))
0 s start end))
(ans (make-string len)))
(string-fold (lambda (c i) (if (char-set-contains? cset c)
(begin (string-set! ans i c)
(+ i 1))
i))
0 s start end)
ans))))
;;; String search
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; string-index string char/char-set/pred [start end]
;;; string-index-right string char/char-set/pred [start end]
;;; string-skip string char/char-set/pred [start end]
;;; string-skip-right string char/char-set/pred [start end]
;;; string-count string char/char-set/pred [start end]
;;; There's a lot of replicated code here for efficiency.
;;; For example, the char/char-set/pred discrimination has
;;; been lifted above the inner loop of each proc.
(define (string-index str criterion . maybe-start+end)
(let-string-start+end (start end) string-index str maybe-start+end
(cond ((char? criterion)
(let lp ((i start))
(and (< i end)
(if (char=? criterion (string-ref str i)) i
(lp (+ i 1))))))
((char-set? criterion)
(let lp ((i start))
(and (< i end)
(if (char-set-contains? criterion (string-ref str i)) i
(lp (+ i 1))))))
((procedure? criterion)
(let lp ((i start))
(and (< i end)
(if (criterion (string-ref str i)) i
(lp (+ i 1))))))
(else (error "Second param is neither char-set, char, or predicate procedure."
string-index criterion)))))
(define (string-index-right str criterion . maybe-start+end)
(let-string-start+end (start end) string-index-right str maybe-start+end
(cond ((char? criterion)
(let lp ((i (- end 1)))
(and (>= i start)
(if (char=? criterion (string-ref str i)) i
(lp (- i 1))))))
((char-set? criterion)
(let lp ((i (- end 1)))
(and (>= i start)
(if (char-set-contains? criterion (string-ref str i)) i
(lp (- i 1))))))
((procedure? criterion)
(let lp ((i (- end 1)))
(and (>= i start)
(if (criterion (string-ref str i)) i
(lp (- i 1))))))
(else (error "Second param is neither char-set, char, or predicate procedure."
string-index-right criterion)))))
(define (string-skip str criterion . maybe-start+end)
(let-string-start+end (start end) string-skip str maybe-start+end
(cond ((char? criterion)
(let lp ((i start))
(and (< i end)
(if (char=? criterion (string-ref str i))
(lp (+ i 1))
i))))
((char-set? criterion)
(let lp ((i start))
(and (< i end)
(if (char-set-contains? criterion (string-ref str i))
(lp (+ i 1))
i))))
((procedure? criterion)
(let lp ((i start))
(and (< i end)
(if (criterion (string-ref str i)) (lp (+ i 1))
i))))
(else (error "Second param is neither char-set, char, or predicate procedure."
string-skip criterion)))))
(define (string-skip-right str criterion . maybe-start+end)
(let-string-start+end (start end) string-skip-right str maybe-start+end
(cond ((char? criterion)
(let lp ((i (- end 1)))
(and (>= i start)
(if (char=? criterion (string-ref str i))
(lp (- i 1))
i))))
((char-set? criterion)
(let lp ((i (- end 1)))
(and (>= i start)
(if (char-set-contains? criterion (string-ref str i))
(lp (- i 1))
i))))
((procedure? criterion)
(let lp ((i (- end 1)))
(and (>= i start)
(if (criterion (string-ref str i)) (lp (- i 1))
i))))
(else (error "CRITERION param is neither char-set or char."
string-skip-right criterion)))))
(define (string-count s criterion . maybe-start+end)
(let-string-start+end (start end) string-count s maybe-start+end
(cond ((char? criterion)
(do ((i start (+ i 1))
(count 0 (if (char=? criterion (string-ref s i))
(+ count 1)
count)))
((>= i end) count)))
((char-set? criterion)
(do ((i start (+ i 1))
(count 0 (if (char-set-contains? criterion (string-ref s i))
(+ count 1)
count)))
((>= i end) count)))
((procedure? criterion)
(do ((i start (+ i 1))
(count 0 (if (criterion (string-ref s i)) (+ count 1) count)))
((>= i end) count)))
(else (error "CRITERION param is neither char-set or char."
string-count criterion)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; string-fill! string char [start end]
;;;
;;; string-copy! to tstart from [fstart fend]
;;; Guaranteed to work, even if s1 eq s2.
(define (string-fill! s char . maybe-start+end)
(check-arg char? char string-fill!)
(let-string-start+end (start end) string-fill! s maybe-start+end
(do ((i (- end 1) (- i 1)))
((< i start))
(string-set! s i char))))
(define (string-copy! to tstart from . maybe-fstart+fend)
(let-string-start+end (fstart fend) string-copy! from maybe-fstart+fend
(check-arg integer? tstart string-copy!)
(check-substring-spec string-copy! to tstart (+ tstart (- fend fstart)))
(%string-copy! to tstart from fstart fend)))
;;; Library-internal routine
(define (%string-copy! to tstart from fstart fend)
(if (> fstart tstart)
(do ((i fstart (+ i 1))
(j tstart (+ j 1)))
((>= i fend))
(string-set! to j (string-ref from i)))
(do ((i (- fend 1) (- i 1))
(j (+ -1 tstart (- fend fstart)) (- j 1)))
((< i fstart))
(string-set! to j (string-ref from i)))))
;;; Returns starting-position in STRING or #f if not true.
;;; This implementation is slow & simple. It is useful as a "spec" or for
;;; comparison testing with fancier implementations.
;;; See below for fast KMP version.
;(define (string-contains string substring . maybe-starts+ends)
; (let-string-start+end2 (start1 end1 start2 end2)
; string-contains string substring maybe-starts+ends
; (let* ((len (- end2 start2))
; (i-bound (- end1 len)))
; (let lp ((i start1))
; (and (< i i-bound)
; (if (string= string substring i (+ i len) start2 end2)
; i
; (lp (+ i 1))))))))
;;; Searching for an occurrence of a substring
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (string-contains text pattern . maybe-starts+ends)
(let-string-start+end2 (t-start t-end p-start p-end)
string-contains text pattern maybe-starts+ends
(%kmp-search pattern text char=? p-start p-end t-start t-end)))
(define (string-contains-ci text pattern . maybe-starts+ends)
(let-string-start+end2 (t-start t-end p-start p-end)
string-contains-ci text pattern maybe-starts+ends
(%kmp-search pattern text char-ci=? p-start p-end t-start t-end)))
;;; Knuth-Morris-Pratt string searching
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; See
;;; "Fast pattern matching in strings"
;;; SIAM J. Computing 6(2):323-350 1977
;;; D. E. Knuth, J. H. Morris and V. R. Pratt
;;; also described in
;;; "Pattern matching in strings"
;;; Alfred V. Aho
;;; Formal Language Theory - Perspectives and Open Problems
;;; Ronald V. Brook (editor)
;;; This algorithm is O(m + n) where m and n are the
;;; lengths of the pattern and string respectively
;;; KMP search source[start,end) for PATTERN. Return starting index of
;;; leftmost match or #f.
(define (%kmp-search pattern text c= p-start p-end t-start t-end)
(let ((plen (- p-end p-start))
(rv (make-kmp-restart-vector pattern c= p-start p-end)))
;; The search loop. TJ & PJ are redundant state.
(let lp ((ti t-start) (pi 0)
(tj (- t-end t-start)) ; (- tlen ti) -- how many chars left.
(pj plen)) ; (- plen pi) -- how many chars left.
(if (= pi plen)
(- ti plen) ; Win.
(and (<= pj tj) ; Lose.
(if (c= (string-ref text ti) ; Search.
(string-ref pattern (+ p-start pi)))
(lp (+ 1 ti) (+ 1 pi) (- tj 1) (- pj 1)) ; Advance.
(let ((pi (vector-ref rv pi))) ; Retreat.
(if (= pi -1)
(lp (+ ti 1) 0 (- tj 1) plen) ; Punt.
(lp ti pi tj (- plen pi))))))))))
;;; (make-kmp-restart-vector pattern [c= start end]) -> integer-vector
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Compute the KMP restart vector RV for string PATTERN. If
;;; we have matched chars 0..i-1 of PATTERN against a search string S, and
;;; PATTERN[i] doesn't match S[k], then reset i := RV[i], and try again to
;;; match S[k]. If RV[i] = -1, then punt S[k] completely, and move on to
;;; S[k+1] and PATTERN[0] -- no possible match of PAT[0..i] contains S[k].
;;;
;;; In other words, if you have matched the first i chars of PATTERN, but
;;; the i+1'th char doesn't match, RV[i] tells you what the next-longest
;;; prefix of PATTERN is that you have matched.
;;;
;;; - C= (default CHAR=?) is used to compare characters for equality.
;;; Pass in CHAR-CI=? for case-folded string search.
;;;
;;; - START & END restrict the pattern to the indicated substring; the
;;; returned vector will be of length END - START. The numbers stored
;;; in the vector will be values in the range [0,END-START) -- that is,
;;; they are valid indices into the restart vector; you have to add START
;;; to them to use them as indices into PATTERN.
;;;
;;; I've split this out as a separate function in case other constant-string
;;; searchers might want to use it.
;;;
;;; E.g.:
;;; a b d a b x
;;; #(-1 0 0 -1 1 2)
(define (make-kmp-restart-vector pattern . maybe-c=+start+end)
(let-optionals* maybe-c=+start+end
((c= char=? (procedure? c=))
((start end) (lambda (args)
(string-parse-start+end make-kmp-restart-vector
pattern args))))
(let* ((rvlen (- end start))
(rv (make-vector rvlen -1)))
(if (> rvlen 0)
(let ((rvlen-1 (- rvlen 1))
(c0 (string-ref pattern start)))
;; Here's the main loop. We have set rv[0] ... rv[i].
;; K = I + START -- it is the corresponding index into PATTERN.
(let lp1 ((i 0) (j -1) (k start))
(if (< i rvlen-1)
;; lp2 invariant:
;; pat[(k-j) .. k-1] matches pat[start .. start+j-1]
;; or j = -1.
(let lp2 ((j j))
(cond ((= j -1)
(let ((i1 (+ 1 i)))
(if (not (c= (string-ref pattern (+ k 1)) c0))
(vector-set! rv i1 0))
(lp1 i1 0 (+ k 1))))
;; pat[(k-j) .. k] matches pat[start..start+j].
((c= (string-ref pattern k) (string-ref pattern (+ j start)))
(let* ((i1 (+ 1 i))
(j1 (+ 1 j)))
(vector-set! rv i1 j1)
(lp1 i1 j1 (+ k 1))))
(else (lp2 (vector-ref rv j)))))))))
rv)))
;;; We've matched I chars from PAT. C is the next char from the search string.
;;; Return the new I after handling C.
;;;
;;; The pattern is (VECTOR-LENGTH RV) chars long, beginning at index PAT-START
;;; in PAT (PAT-START is usually 0). The I chars of the pattern we've matched
;;; are
;;; PAT[PAT-START .. PAT-START + I].
;;;
;;; It's *not* an oversight that there is no friendly error checking or
;;; defaulting of arguments. This is a low-level, inner-loop procedure
;;; that we want integrated/inlined into the point of call.
(define (kmp-step pat rv c i c= p-start)
(let lp ((i i))
(if (c= c (string-ref pat (+ i p-start))) ; Match =>
(+ i 1) ; Done.
(let ((i (vector-ref rv i))) ; Back up in PAT.
(if (= i -1) 0 ; Can't back up further.
(lp i)))))) ; Keep trying for match.
;;; Zip through S[start,end), looking for a match of PAT. Assume we've
;;; already matched the first I chars of PAT when we commence at S[start].
;;; - <0: If we find a match *ending* at index J, return -J.
;;; - >=0: If we get to the end of the S[start,end) span without finding
;;; a complete match, return the number of chars from PAT we'd matched
;;; when we ran off the end.
;;;
;;; This is useful for searching *across* buffers -- that is, when your
;;; input comes in chunks of text. We hand-integrate the KMP-STEP loop
;;; for speed.
(define (string-kmp-partial-search pat rv s i . c=+p-start+s-start+s-end)
(check-arg vector? rv string-kmp-partial-search)
(let-optionals* c=+p-start+s-start+s-end
((c= char=? (procedure? c=))
(p-start 0 (and (integer? p-start) (exact? p-start) (<= 0 p-start)))
((s-start s-end) (lambda (args)
(string-parse-start+end string-kmp-partial-search
s args))))
(let ((patlen (vector-length rv)))
(check-arg (lambda (i) (and (integer? i) (exact? i) (<= 0 i) (< i patlen)))
i string-kmp-partial-search)
;; Enough prelude. Here's the actual code.
(let lp ((si s-start) ; An index into S.
(vi i)) ; An index into RV.
(cond ((= vi patlen) (- si)) ; Win.
((= si s-end) vi) ; Ran off the end.
(else ; Match s[si] & loop.
(let ((c (string-ref s si)))
(lp (+ si 1)
(let lp2 ((vi vi)) ; This is just KMP-STEP.
(if (c= c (string-ref pat (+ vi p-start)))
(+ vi 1)
(let ((vi (vector-ref rv vi)))
(if (= vi -1) 0
(lp2 vi)))))))))))))
;;; Misc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; (string-null? s)
;;; (string-reverse s [start end])
;;; (string-reverse! s [start end])
;;; (reverse-list->string clist)
;;; (string->list s [start end])
(define (string-null? s) (zero? (string-length s)))
(define (string-reverse s . maybe-start+end)
(let-string-start+end (start end) string-reverse s maybe-start+end
(let* ((len (- end start))
(ans (make-string len)))
(do ((i start (+ i 1))
(j (- len 1) (- j 1)))
((< j 0))
(string-set! ans j (string-ref s i)))
ans)))
(define (string-reverse! s . maybe-start+end)
(let-string-start+end (start end) string-reverse! s maybe-start+end
(do ((i (- end 1) (- i 1))
(j start (+ j 1)))
((<= i j))
(let ((ci (string-ref s i)))
(string-set! s i (string-ref s j))
(string-set! s j ci)))))
(define (reverse-list->string clist)
(let* ((len (length clist))
(s (make-string len)))
(do ((i (- len 1) (- i 1)) (clist clist (cdr clist)))
((not (pair? clist)))
(string-set! s i (car clist)))
s))
;(define (string->list s . maybe-start+end)
; (apply string-fold-right cons '() s maybe-start+end))
(define (string->list s . maybe-start+end)
(let-string-start+end (start end) string->list s maybe-start+end
(do ((i (- end 1) (- i 1))
(ans '() (cons (string-ref s i) ans)))
((< i start) ans))))
;;; Defined by R5RS, so commented out here.
;(define (list->string lis) (string-unfold null? car cdr lis))
;;; string-concatenate string-list -> string
;;; string-concatenate/shared string-list -> string
;;; string-append/shared s ... -> string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; STRING-APPEND/SHARED has license to return a string that shares storage
;;; with any of its arguments. In particular, if there is only one non-empty
;;; string amongst its parameters, it is permitted to return that string as
;;; its result. STRING-APPEND, by contrast, always allocates new storage.
;;;
;;; STRING-CONCATENATE & STRING-CONCATENATE/SHARED are passed a list of
;;; strings, which they concatenate into a result string. STRING-CONCATENATE
;;; always allocates a fresh string; STRING-CONCATENATE/SHARED may (or may
;;; not) return a result that shares storage with any of its arguments. In
;;; particular, if it is applied to a singleton list, it is permitted to
;;; return the car of that list as its value.
(define (string-append/shared . strings) (string-concatenate/shared strings))
(define (string-concatenate/shared strings)
(let lp ((strings strings) (nchars 0) (first #f))
(cond ((pair? strings) ; Scan the args, add up total
(let* ((string (car strings)) ; length, remember 1st
(tail (cdr strings)) ; non-empty string.
(slen (string-length string)))
(if (zero? slen)
(lp tail nchars first)
(lp tail (+ nchars slen) (or first strings)))))
((zero? nchars) "")
;; Just one non-empty string! Return it.
((= nchars (string-length (car first))) (car first))
(else (let ((ans (make-string nchars)))
(let lp ((strings first) (i 0))
(if (pair? strings)
(let* ((s (car strings))
(slen (string-length s)))
(%string-copy! ans i s 0 slen)
(lp (cdr strings) (+ i slen)))))
ans)))))
; Alas, Scheme 48's APPLY blows up if you have many, many arguments.
;(define (string-concatenate strings) (apply string-append strings))
;;; Here it is written out. I avoid using REDUCE to add up string lengths
;;; to avoid non-R5RS dependencies.
(define (string-concatenate strings)
(let* ((total (do ((strings strings (cdr strings))
(i 0 (+ i (string-length (car strings)))))
((not (pair? strings)) i)))
(ans (make-string total)))
(let lp ((i 0) (strings strings))
(if (pair? strings)
(let* ((s (car strings))
(slen (string-length s)))
(%string-copy! ans i s 0 slen)
(lp (+ i slen) (cdr strings)))))
ans))
;;; Defined by R5RS, so commented out here.
;(define (string-append . strings) (string-concatenate strings))
;;; string-concatenate-reverse string-list [final-string end] -> string
;;; string-concatenate-reverse/shared string-list [final-string end] -> string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Return
;;; (string-concatenate
;;; (reverse
;;; (cons (substring final-string 0 end) string-list)))
(define (string-concatenate-reverse string-list . maybe-final+end)
(let-optionals* maybe-final+end ((final "" (string? final))
(end (string-length final)
(and (integer? end)
(exact? end)
(<= 0 end (string-length final)))))
(let ((len (let lp ((sum 0) (lis string-list))
(if (pair? lis)
(lp (+ sum (string-length (car lis))) (cdr lis))
sum))))
(%finish-string-concatenate-reverse len string-list final end))))
(define (string-concatenate-reverse/shared string-list . maybe-final+end)
(let-optionals* maybe-final+end ((final "" (string? final))
(end (string-length final)
(and (integer? end)
(exact? end)
(<= 0 end (string-length final)))))
;; Add up the lengths of all the strings in STRING-LIST; also get a
;; pointer NZLIST into STRING-LIST showing where the first non-zero-length
;; string starts.
(let lp ((len 0) (nzlist #f) (lis string-list))
(if (pair? lis)
(let ((slen (string-length (car lis))))
(lp (+ len slen)
(if (or nzlist (zero? slen)) nzlist lis)
(cdr lis)))
(cond ((zero? len) (substring/shared final 0 end))
;; LEN > 0, so NZLIST is non-empty.
((and (zero? end) (= len (string-length (car nzlist))))
(car nzlist))
(else (%finish-string-concatenate-reverse len nzlist final end)))))))
(define (%finish-string-concatenate-reverse len string-list final end)
(let ((ans (make-string (+ end len))))
(%string-copy! ans len final 0 end)
(let lp ((i len) (lis string-list))
(if (pair? lis)
(let* ((s (car lis))
(lis (cdr lis))
(slen (string-length s))
(i (- i slen)))
(%string-copy! ans i s 0 slen)
(lp i lis))))
ans))
;;; string-replace s1 s2 start1 end1 [start2 end2] -> string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Replace S1[START1,END1) with S2[START2,END2).
(define (string-replace s1 s2 start1 end1 . maybe-start+end)
(check-substring-spec string-replace s1 start1 end1)
(let-string-start+end (start2 end2) string-replace s2 maybe-start+end
(let* ((slen1 (string-length s1))
(sublen2 (- end2 start2))
(alen (+ (- slen1 (- end1 start1)) sublen2))
(ans (make-string alen)))
(%string-copy! ans 0 s1 0 start1)
(%string-copy! ans start1 s2 start2 end2)
(%string-copy! ans (+ start1 sublen2) s1 end1 slen1)
ans)))
;;; string-tokenize s [token-set start end] -> list
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Break S up into a list of token strings, where a token is a maximal
;;; non-empty contiguous sequence of chars belonging to TOKEN-SET.
;;; (string-tokenize "hello, world") => ("hello," "world")
(define (string-tokenize s . token-chars+start+end)
(let-optionals* token-chars+start+end
((token-chars char-set:graphic (char-set? token-chars)) rest)
(let-string-start+end (start end) string-tokenize s rest
(let lp ((i end) (ans '()))
(cond ((and (< start i) (string-index-right s token-chars start i)) =>
(lambda (tend-1)
(let ((tend (+ 1 tend-1)))
(cond ((string-skip-right s token-chars start tend-1) =>
(lambda (tstart-1)
(lp tstart-1
(cons (substring s (+ 1 tstart-1) tend)
ans))))
(else (cons (substring s start tend) ans))))))
(else ans))))))
;;; xsubstring s from [to start end] -> string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; S is a string; START and END are optional arguments that demarcate
;;; a substring of S, defaulting to 0 and the length of S (e.g., the whole
;;; string). Replicate this substring up and down index space, in both the
;; positive and negative directions. For example, if S = "abcdefg", START=3,
;;; and END=6, then we have the conceptual bidirectionally-infinite string
;;; ... d e f d e f d e f d e f d e f d e f d e f ...
;;; ... -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 ...
;;; XSUBSTRING returns the substring of this string beginning at index FROM,
;;; and ending at TO (which defaults to FROM+(END-START)).
;;;
;;; You can use XSUBSTRING in many ways:
;;; - To rotate a string left: (xsubstring "abcdef" 2) => "cdefab"
;;; - To rotate a string right: (xsubstring "abcdef" -2) => "efabcd"
;;; - To replicate a string: (xsubstring "abc" 0 7) => "abcabca"
;;;
;;; Note that
;;; - The FROM/TO indices give a half-open range -- the characters from
;;; index FROM up to, but not including index TO.
;;; - The FROM/TO indices are not in terms of the index space for string S.
;;; They are in terms of the replicated index space of the substring
;;; defined by S, START, and END.
;;;
;;; It is an error if START=END -- although this is allowed by special
;;; dispensation when FROM=TO.
(define (xsubstring s from . maybe-to+start+end)
(check-arg (lambda (val) (and (integer? val) (exact? val)))
from xsubstring)
(receive (to start end)
(if (pair? maybe-to+start+end)
(let-string-start+end (start end) xsubstring s (cdr maybe-to+start+end)
(let ((to (car maybe-to+start+end)))
(check-arg (lambda (val) (and (integer? val)
(exact? val)
(<= from val)))
to xsubstring)
(values to start end)))
(let ((slen (string-length (check-arg string? s xsubstring))))
(values (+ from slen) 0 slen)))
(let ((slen (- end start))
(anslen (- to from)))
(cond ((zero? anslen) "")
((zero? slen) (error "Cannot replicate empty (sub)string"
xsubstring s from to start end))
((= 1 slen) ; Fast path for 1-char replication.
(make-string anslen (string-ref s start)))
;; Selected text falls entirely within one span.
((= (floor (/ from slen)) (floor (/ to slen)))
(substring s (+ start (modulo from slen))
(+ start (modulo to slen))))
;; Selected text requires multiple spans.
(else (let ((ans (make-string anslen)))
(%multispan-repcopy! ans 0 s from to start end)
ans))))))
;;; string-xcopy! target tstart s sfrom [sto start end] -> unspecific
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Exactly the same as xsubstring, but the extracted text is written
;;; into the string TARGET starting at index TSTART.
;;; This operation is not defined if (EQ? TARGET S) -- you cannot copy
;;; a string on top of itself.
(define (string-xcopy! target tstart s sfrom . maybe-sto+start+end)
(check-arg (lambda (val) (and (integer? val) (exact? val)))
sfrom string-xcopy!)
(receive (sto start end)
(if (pair? maybe-sto+start+end)
(let-string-start+end (start end) string-xcopy! s (cdr maybe-sto+start+end)
(let ((sto (car maybe-sto+start+end)))
(check-arg (lambda (val) (and (integer? val) (exact? val)))
sto string-xcopy!)
(values sto start end)))
(let ((slen (string-length s)))
(values (+ sfrom slen) 0 slen)))
(let* ((tocopy (- sto sfrom))
(tend (+ tstart tocopy))
(slen (- end start)))
(check-substring-spec string-xcopy! target tstart tend)
(cond ((zero? tocopy))
((zero? slen) (error "Cannot replicate empty (sub)string"
string-xcopy!
target tstart s sfrom sto start end))
((= 1 slen) ; Fast path for 1-char replication.
(string-fill! target (string-ref s start) tstart tend))
;; Selected text falls entirely within one span.
((= (floor (/ sfrom slen)) (floor (/ sto slen)))
(%string-copy! target tstart s
(+ start (modulo sfrom slen))
(+ start (modulo sto slen))))
;; Multi-span copy.
(else (%multispan-repcopy! target tstart s sfrom sto start end))))))
;;; This is the core copying loop for XSUBSTRING and STRING-XCOPY!
;;; Internal -- not exported, no careful arg checking.
(define (%multispan-repcopy! target tstart s sfrom sto start end)
(let* ((slen (- end start))
(i0 (+ start (modulo sfrom slen)))
(total-chars (- sto sfrom)))
;; Copy the partial span @ the beginning
(%string-copy! target tstart s i0 end)
(let* ((ncopied (- end i0)) ; We've copied this many.
(nleft (- total-chars ncopied)) ; # chars left to copy.
(nspans (quotient nleft slen))) ; # whole spans to copy
;; Copy the whole spans in the middle.
(do ((i (+ tstart ncopied) (+ i slen)) ; Current target index.
(nspans nspans (- nspans 1))) ; # spans to copy
((zero? nspans)
;; Copy the partial-span @ the end & we're done.
(%string-copy! target i s start (+ start (- total-chars (- i tstart)))))
(%string-copy! target i s start end))))); Copy a whole span.
;;; (string-join string-list [delimiter grammar]) => string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Paste strings together using the delimiter string.
;;;
;;; (string-join '("foo" "bar" "baz") ":") => "foo:bar:baz"
;;;
;;; DELIMITER defaults to a single space " "
;;; GRAMMAR is one of the symbols {prefix, infix, strict-infix, suffix}
;;; and defaults to 'infix.
;;;
;;; I could rewrite this more efficiently -- precompute the length of the
;;; answer string, then allocate & fill it in iteratively. Using
;;; STRING-CONCATENATE is less efficient.
(define (string-join strings . delim+grammar)
(let-optionals* delim+grammar ((delim " " (string? delim))
(grammar 'infix))
(let ((buildit (lambda (lis final)
(let recur ((lis lis))
(if (pair? lis)
(cons delim (cons (car lis) (recur (cdr lis))))
final)))))
(cond ((pair? strings)
(string-concatenate
(case grammar
((infix strict-infix)
(cons (car strings) (buildit (cdr strings) '())))
((prefix) (buildit strings '()))
((suffix)
(cons (car strings) (buildit (cdr strings) (list delim))))
(else (error "Illegal join grammar"
grammar string-join)))))
((not (null? strings))
(error "STRINGS parameter not list." strings string-join))
;; STRINGS is ()
((eq? grammar 'strict-infix)
(error "Empty list cannot be joined with STRICT-INFIX grammar."
string-join))
(else ""))))) ; Special-cased for infix grammar.
;;; Porting & performance-tuning notes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; See the section at the beginning of this file on external dependencies.
;;;
;;; The biggest issue with respect to porting is the LET-OPTIONALS* macro.
;;; There are many, many optional arguments in this library; the complexity
;;; of parsing, defaulting & type-testing these parameters is handled with the
;;; aid of this macro. There are about 15 uses of LET-OPTIONALS*. You can
;;; rewrite the uses, port the hairy macro definition (which is implemented
;;; using a Clinger-Rees low-level explicit-renaming macro system), or port
;;; the simple, high-level definition, which is less efficient.
;;;
;;; There is a fair amount of argument checking. This is, strictly speaking,
;;; unnecessary -- the actual body of the procedures will blow up if, say, a
;;; START/END index is improper. However, the error message will not be as
;;; good as if the error were caught at the "higher level." Also, a very, very
;;; smart Scheme compiler may be able to exploit having the type checks done
;;; early, so that the actual body of the procedures can assume proper values.
;;; This isn't likely; this kind of compiler technology isn't common any
;;; longer.
;;;
;;; The overhead of optional-argument parsing is irritating. The optional
;;; arguments must be consed into a rest list on entry, and then parsed out.
;;; Function call should be a matter of a few register moves and a jump; it
;;; should not involve heap allocation! Your Scheme system may have a superior
;;; non-R5RS optional-argument system that can eliminate this overhead. If so,
;;; then this is a prime candidate for optimising these procedures,
;;; *especially* the many optional START/END index parameters.
;;;
;;; Note that optional arguments are also a barrier to procedure integration.
;;; If your Scheme system permits you to specify alternate entry points
;;; for a call when the number of optional arguments is known in a manner
;;; that enables inlining/integration, this can provide performance
;;; improvements.
;;;
;;; There is enough *explicit* error checking that *all* string-index
;;; operations should *never* produce a bounds error. Period. Feel like
;;; living dangerously? *Big* performance win to be had by replacing
;;; STRING-REF's and STRING-SET!'s with unsafe equivalents in the loops.
;;; Similarly, fixnum-specific operators can speed up the arithmetic done on
;;; the index values in the inner loops. The only arguments that are not
;;; completely error checked are
;;; - string lists (complete checking requires time proportional to the
;;; length of the list)
;;; - procedure arguments, such as char->char maps & predicates.
;;; There is no way to check the range & domain of procedures in Scheme.
;;; Procedures that take these parameters cannot fully check their
;;; arguments. But all other types to all other procedures are fully
;;; checked.
;;;
;;; This does open up the alternate possibility of simply *removing* these
;;; checks, and letting the safe primitives raise the errors. On a dumb
;;; Scheme system, this would provide speed (by eliminating the redundant
;;; error checks) at the cost of error-message clarity.
;;;
;;; See the comments preceding the hash function code for notes on tuning
;;; the default bound so that the code never overflows your implementation's
;;; fixnum size into bignum calculation.
;;;
;;; In an interpreted Scheme, some of these procedures, or the internal
;;; routines with % prefixes, are excellent candidates for being rewritten
;;; in C. Consider STRING-HASH, %STRING-COMPARE, the
;;; %STRING-{SUF,PRE}FIX-LENGTH routines, STRING-COPY!, STRING-INDEX &
;;; STRING-SKIP (char-set & char cases), SUBSTRING and SUBSTRING/SHARED,
;;; %KMP-SEARCH, and %MULTISPAN-REPCOPY!.
;;;
;;; It would also be nice to have the ability to mark some of these
;;; routines as candidates for inlining/integration.
;;;
;;; All the %-prefixed routines in this source code are written
;;; to be called internally to this library. They do *not* perform
;;; friendly error checks on the inputs; they assume everything is
;;; proper. They also do not take optional arguments. These two properties
;;; save calling overhead and enable procedure integration -- but they
;;; are not appropriate for exported routines.
;;; Copyright details
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The prefix/suffix and comparison routines in this code had (extremely
;;; distant) origins in MIT Scheme's string lib, and was substantially
;;; reworked by Olin Shivers (shivers@ai.mit.edu) 9/98. As such, it is
;;; covered by MIT Scheme's open source copyright. See below for details.
;;;
;;; The KMP string-search code was influenced by implementations written
;;; by Stephen Bevan, Brian Dehneyer and Will Fitzgerald. However, this
;;; version was written from scratch by myself.
;;;
;;; The remainder of this code was written from scratch by myself for scsh.
;;; The scsh copyright is a BSD-style open source copyright. See below for
;;; details.
;;; -Olin Shivers
;;; MIT Scheme copyright terms
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This material was developed by the Scheme project at the Massachusetts
;;; Institute of Technology, Department of Electrical Engineering and
;;; Computer Science. Permission to copy and modify this software, to
;;; redistribute either the original software or a modified version, and
;;; to use this software for any purpose is granted, subject to the
;;; following restrictions and understandings.
;;;
;;; 1. Any copy made of this software must include this copyright notice
;;; in full.
;;;
;;; 2. Users of this software agree to make their best efforts (a) to
;;; return to the MIT Scheme project any improvements or extensions that
;;; they make, so that these may be included in future releases; and (b)
;;; to inform MIT of noteworthy uses of this software.
;;;
;;; 3. All materials developed as a consequence of the use of this
;;; software shall duly acknowledge such use, in accordance with the usual
;;; standards of acknowledging credit in academic research.
;;;
;;; 4. MIT has made no warrantee or representation that the operation of
;;; this software will be error-free, and MIT is under no obligation to
;;; provide any services, by way of maintenance, update, or otherwise.
;;;
;;; 5. In conjunction with products arising from the use of this material,
;;; there shall be no use of the name of the Massachusetts Institute of
;;; Technology nor of any adaptation thereof in any advertising,
;;; promotional, or sales literature without prior written consent from
;;; MIT in each case.
;;; Scsh copyright terms
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; All rights reserved.
;;;
;;; Redistribution and use in source and binary forms, with or without
;;; modification, are permitted provided that the following conditions
;;; are met:
;;; 1. Redistributions of source code must retain the above copyright
;;; notice, this list of conditions and the following disclaimer.
;;; 2. Redistributions in binary form must reproduce the above copyright
;;; notice, this list of conditions and the following disclaimer in the
;;; documentation and/or other materials provided with the distribution.
;;; 3. The name of the authors may not be used to endorse or promote products
;;; derived from this software without specific prior written permission.
;;;
;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
chez-srfi-0.0+git20241031.b424440+dfsg/%3a13/strings.sls 0000664 0000000 0000000 00000004664 14737542645 0021443 0 ustar 00root root 0000000 0000000 #!r6rs
;; Copyright 2010 Derick Eddington. My MIT-style license is in the file named
;; LICENSE from the original collection this file is distributed with.
(library (srfi :13 strings)
(export
string-map string-map!
string-fold string-unfold
string-fold-right string-unfold-right
string-tabulate string-for-each string-for-each-index
string-every string-any
string-hash string-hash-ci
string-compare string-compare-ci
string= string< string> string<= string>= string<>
string-ci= string-ci< string-ci> string-ci<= string-ci>= string-ci<>
string-downcase string-upcase string-titlecase
string-downcase! string-upcase! string-titlecase!
string-take string-take-right
string-drop string-drop-right
string-pad string-pad-right
string-trim string-trim-right string-trim-both
string-filter string-delete
string-index string-index-right
string-skip string-skip-right
string-count
string-prefix-length string-prefix-length-ci
string-suffix-length string-suffix-length-ci
string-prefix? string-prefix-ci?
string-suffix? string-suffix-ci?
string-contains string-contains-ci
string-copy! substring/shared
string-reverse string-reverse! reverse-list->string
string-concatenate string-concatenate/shared string-concatenate-reverse
string-concatenate-reverse/shared
string-append/shared
xsubstring string-xcopy!
string-null?
string-join
string-tokenize
string-replace
; R5RS extended:
string->list string-copy string-fill!
; R5RS re-exports:
string? make-string string-length string-ref string-set!
string string-append list->string
; Low-level routines:
#;(make-kmp-restart-vector string-kmp-partial-search kmp-step
string-parse-start+end
string-parse-final-start+end
let-string-start+end
check-substring-spec
substring-spec-ok?)
)
(import
(except (rnrs) string-copy string-for-each string->list
string-upcase string-downcase string-titlecase string-hash)
(except (rnrs mutable-strings) string-fill!)
(rnrs r5rs)
(srfi :23 error tricks)
(srfi :8 receive)
(srfi :14 char-sets)
(srfi private check-arg)
(srfi private let-opt)
(srfi private include))
(define (char-cased? c)
(char-upper-case? (char-upcase c)))
(SRFI-23-error->R6RS "(library (srfi :13 strings))"
(include/resolve ("srfi" "%3a13") "srfi-13.scm"))
)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a130.sls 0000664 0000000 0000000 00000002176 14737542645 0020026 0 ustar 00root root 0000000 0000000 (library (srfi :130)
(export
string-cursor? string-cursor-start string-cursor-end string-cursor-next
string-cursor-prev string-cursor-forward string-cursor-back string-cursor=?
string-cursor string-cursor>? string-cursor<=? string-cursor>=?
string-cursor-diff string-cursor->index string-index->cursor
string-null? string-every string-any
string-tabulate string-unfold string-unfold-right
string->list/cursors string->vector/cursors reverse-list->string
string-join
string-ref/cursor substring/cursors string-copy/cursors string-take
string-take-right string-drop string-drop-right string-pad string-pad-right
string-trim string-trim-right string-trim-both
string-prefix-length string-suffix-length string-prefix? string-suffix?
string-index string-index-right string-skip string-skip-right
string-contains string-contains-right
string-reverse string-concatenate string-concatenate-reverse string-fold
string-fold-right string-for-each-cursor string-replicate string-count
string-replace string-split string-filter string-remove)
(import (srfi :130 string-cursors)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a130/ 0000775 0000000 0000000 00000000000 14737542645 0017275 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a130/130.body.scm 0000664 0000000 0000000 00000017637 14737542645 0021256 0 ustar 00root root 0000000 0000000 ;;; Copyright (C) William D Clinger (2016).
;;;
;;; Permission is hereby granted, free of charge, to any person
;;; obtaining a copy of this software and associated documentation
;;; files (the "Software"), to deal in the Software without
;;; restriction, including without limitation the rights to use,
;;; copy, modify, merge, publish, distribute, sublicense, and/or
;;; sell copies of the Software, and to permit persons to whom the
;;; Software is furnished to do so, subject to the following
;;; conditions:
;;;
;;; The above copyright notice and this permission notice shall be
;;; included in all copies or substantial portions of the Software.
;;;
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
;;; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
;;; OTHER DEALINGS IN THE SOFTWARE.
;;; For convenient interoperation with SRFI 13,
;;; cursors ought to be the same as indexes.
;;; Some of the FIXME comments mark procedures that really ought
;;; to do more checking for "is an error" situations.
;; added for chez scheme support
(define string->vector
(lambda (str)
(let ([len (string-length str)])
(let ([v (make-vector len)])
(do ([i (fx- len 1) (fx- i 1)])
((fx=? i -1))
(vector-set! v i (string-ref str i)))
v))))
(define (string-cursor? x)
(and (and (integer? x) (exact? x))
(>= x 0)))
(define (string-cursor-start s) 0)
(define (string-cursor-end s) (string-length s))
(define (string-cursor-next s curs) (+ curs 1)) ; FIXME
(define (string-cursor-prev s curs) (- curs 1)) ; FIXME
(define (string-cursor-forward s curs n) (+ curs n)) ; FIXME
(define (string-cursor-back s curs n) (- curs n)) ; FIXME
(define (string-cursor=? curs1 curs2) (= curs1 curs2))
(define (string-cursor curs1 curs2) (< curs1 curs2))
(define (string-cursor>? curs1 curs2) (> curs1 curs2))
(define (string-cursor<=? curs1 curs2) (<= curs1 curs2))
(define (string-cursor>=? curs1 curs2) (>= curs1 curs2))
(define (string-cursor-diff s start end) (- end start)) ; FIXME
(define (string-cursor->index s curs) curs)
(define (string-index->cursor s idx) idx)
(define string->list/cursors string->list)
(define string->vector/cursors string->vector)
(define string-ref/cursor string-ref)
(define substring/cursors substring)
(define string-copy/cursors string-copy)
;;; The SRFI 13 procedures return #f sometimes, so they can't be the same
;;; even if cursors are the same as indexes.
;;; Furthermore string-index-right and string-skip-right return the
;;; successor of the cursor for the character found.
(define string-index
(case-lambda
((s pred)
(string-index s pred 0 (string-length s)))
((s pred start)
(string-index s pred start (string-length s)))
((s pred start end)
(or (srfi-13:string-index s pred start end)
end))))
(define string-index-right
(case-lambda
((s pred)
(string-index-right s pred 0 (string-length s)))
((s pred start)
(string-index-right s pred start (string-length s)))
((s pred start end)
(let ((i (srfi-13:string-index-right s pred start end)))
(if i (+ i 1) start)))))
(define (string-skip s pred . rest)
(apply string-index s (lambda (x) (not (pred x))) rest))
(define (string-skip-right s pred . rest)
(apply string-index-right s (lambda (x) (not (pred x))) rest))
;;; FIXME: inefficient
(define string-contains-right
(case-lambda
((s1 s2)
(string-contains-right s1 s2 0 (string-length s1) 0 (string-length s2)))
((s1 s2 start1)
(string-contains-right s1 s2
start1 (string-length s1) 0 (string-length s2)))
((s1 s2 start1 end1)
(string-contains-right s1 s2 start1 end1 0 (string-length s2)))
((s1 s2 start1 end1 start2)
(string-contains-right s1 s2 start1 end1 start2 (string-length s2)))
((s1 s2 start1 end1 start2 end2)
(if (= start2 end2)
end1
(let loop ((i #f)
(j (string-contains s1 s2 start1 end1 start2 end2)))
(if (and j (< j end1))
(loop j (string-contains s1 s2 (+ j 1) end1 start2 end2))
i))))))
(define string-for-each-cursor
(case-lambda
((proc s)
(string-for-each-cursor proc s 0 (string-length s)))
((proc s start)
(string-for-each-cursor proc s start (string-length s)))
((proc s start end)
(do ((i start (+ i 1)))
((>= i end))
(proc i)))))
(define string-replicate
(case-lambda
((s from to start end)
(string-replicate (substring s start end) from to))
((s from to start)
(string-replicate (substring s start (string-length s)) from to))
((s from to)
(let* ((n (- to from))
(len (string-length s)))
(cond ((= n 0)
"")
((or (< n 0)
(= len 0))
(assertion-violation 'string-replicate
"unexpected arguments"
s from to))
(else
(let* ((from (mod from len)) ; make from non-negative
(to (+ from n)))
(do ((replicates '() (cons s replicates))
(replicates-length 0 (+ replicates-length len)))
((>= replicates-length to)
(substring (apply string-append replicates)
from to))))))))))
(define string-split
(case-lambda
((s delimiter grammar limit start end)
(string-split (substring s start end) delimiter grammar limit))
((s delimiter grammar limit start)
(string-split (substring s start (string-length s))
delimiter grammar limit))
((s delimiter)
(string-split s delimiter 'infix #f))
((s delimiter grammar)
(string-split s delimiter grammar #f))
((s delimiter grammar limit)
(define (complain)
(assertion-violation 'string-split
"unexpected arguments"
s delimiter grammar limit))
(let* ((limit (or limit (string-length s)))
(splits
(cond ((= 0 (string-length delimiter))
(string-split-into-characters s limit))
(else
(string-split-using-word s delimiter limit)))))
(case grammar
((infix strict-infix)
(if (= 0 (string-length s))
(if (eq? grammar 'infix)
'()
(complain))
splits))
((prefix)
(if (and (pair? splits)
(= 0 (string-length (car splits))))
(cdr splits)
splits))
((suffix)
(if (and (pair? splits)
(= 0 (string-length (car (last-pair splits)))))
(reverse (cdr (reverse splits)))
splits))
(else
(complain)))))))
(define (string-split-into-characters s limit)
(let ((n (string-length s)))
(cond ((> n (+ limit 1))
(append (string-split-into-characters (substring s 0 limit) limit)
(substring s limit n)))
(else
(map string (string->list s))))))
;;; FIXME: inefficient
(define (string-split-using-word s sep limit)
(cond ((= 0 limit)
(list s))
(else
(let ((i (string-contains s sep)))
(if i
(cons (substring s 0 i)
(string-split-using-word
(substring s (+ i (string-length sep)) (string-length s))
sep
(- limit 1)))
(list s))))))
(define (string-remove pred s . args)
(apply string-filter
(lambda (c) (not (pred c)))
s
args))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a130/string-cursors.sls 0000664 0000000 0000000 00000004266 14737542645 0023034 0 ustar 00root root 0000000 0000000 (library (srfi :130 string-cursors)
(export
string-cursor? string-cursor-start string-cursor-end string-cursor-next
string-cursor-prev string-cursor-forward string-cursor-back string-cursor=?
string-cursor string-cursor>? string-cursor<=? string-cursor>=?
string-cursor-diff string-cursor->index string-index->cursor
string-null? string-every string-any
string-tabulate string-unfold string-unfold-right
string->list/cursors string->vector/cursors reverse-list->string
string-join
string-ref/cursor substring/cursors string-copy/cursors string-take
string-take-right string-drop string-drop-right string-pad string-pad-right
string-trim string-trim-right string-trim-both
string-prefix-length string-suffix-length string-prefix? string-suffix?
string-index string-index-right string-skip string-skip-right
string-contains string-contains-right
string-reverse string-concatenate string-concatenate-reverse string-fold
string-fold-right string-for-each-cursor string-replicate string-count
string-replace string-split string-filter string-remove)
(import (except (rnrs) string-copy)
(rename (only (srfi :13) string-copy
string-index string-index-right
string-contains string-filter string-replace
string-count string-fold-right string-fold
string-concatenate-reverse string-concatenate
string-reverse string-suffix? string-prefix?
string-suffix-length string-prefix-length
string-trim-both string-trim-right string-trim
string-pad-right string-pad string-drop-right
string-drop string-take-right string-take string-join
reverse-list->string string-unfold-right string-unfold
string-tabulate string-any string-every string-null?)
(string-index srfi-13:string-index)
(string-index-right srfi-13:string-index-right))
(only (srfi :1) last-pair)
(srfi private include))
(include/resolve ("srfi" "%3a130") "130.body.scm"))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a131.sls 0000664 0000000 0000000 00000000123 14737542645 0020015 0 ustar 00root root 0000000 0000000 (library (srfi :131)
(export define-record-type)
(import (srfi :131 records)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a131/ 0000775 0000000 0000000 00000000000 14737542645 0017276 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a131/records.sls 0000664 0000000 0000000 00000007772 14737542645 0021477 0 ustar 00root root 0000000 0000000 (library (srfi :131 records)
(export define-record-type)
(import (except (rnrs) define-record-type)
(srfi :99 records procedural))
(define-syntax define-record-type
(syntax-rules ()
((_ (type-name parent) constructor-spec predicate-spec . field-specs)
(define-record-type-helper0
type-name parent constructor-spec predicate-spec . field-specs))
((_ type-name constructor-spec predicate-spec . field-specs)
(define-record-type-helper0
type-name #f constructor-spec predicate-spec . field-specs))))
;; breaks the field-specs into two separate lists of accessors and mutators
(define-syntax define-record-type-helper0
(syntax-rules ()
((_ type-name parent constructor-spec predicate-spec . field-specs)
(define-record-type-helper1
type-name parent constructor-spec predicate-spec field-specs ()))))
;; reverses the field-specs before delegating to a second helper
(define-syntax define-record-type-helper1
(syntax-rules ()
((_ type-name parent constructor-spec predicate-spec () revspecs)
(define-record-type-helper2
type-name parent constructor-spec predicate-spec revspecs () () ()))
((_ type-name parent constructor-spec predicate-spec
(spec . field-specs) revspecs)
(define-record-type-helper1
type-name parent constructor-spec predicate-spec
field-specs (spec . revspecs)))))
(define-syntax define-record-type-helper2
(syntax-rules ()
((_ type-name
parent constructor-spec predicate-spec
() accessors mutators fields)
(define-record-type-helper
type-name fields
parent constructor-spec predicate-spec accessors mutators))
((_ type-name parent constructor-spec predicate-spec
((field-name accessor-name) . field-specs)
accessors mutators fields)
(define-record-type-helper2
type-name parent constructor-spec predicate-spec
field-specs
((accessor-name field-name) . accessors)
mutators
(field-name . fields)))
((_ type-name parent constructor-spec predicate-spec
((field-name accessor-name mutator-name) . field-specs)
accessors mutators fields)
(define-record-type-helper2
type-name parent constructor-spec predicate-spec
field-specs
((accessor-name field-name) . accessors)
((mutator-name field-name) . mutators)
(field-name . fields)))))
;; Uses the SRFI 99 procedural layer for the real work.
(define-syntax define-record-type-helper
(syntax-rules ()
((_ type-name fields parent #f predicate
((accessor field) ...) ((mutator mutable-field) ...))
(define-record-type-helper
type-name fields parent ignored predicate
((accessor field) ...) ((mutator mutable-field) ...)))
((_ type-name fields parent constructor #f
((accessor field) ...) ((mutator mutable-field) ...))
(define-record-type-helper
type-name fields parent constructor ignored
((accessor field) ...) ((mutator mutable-field) ...)))
((_ type-name fields parent (constructor args) predicate
((accessor field) ...) ((mutator mutable-field) ...))
(begin (define type-name (make-rtd 'type-name 'fields parent))
(define constructor (rtd-constructor type-name 'args))
(define predicate (rtd-predicate type-name))
(define accessor (rtd-accessor type-name 'field))
...
(define mutator (rtd-mutator type-name 'mutable-field))
...))
((_ type-name fields parent constructor predicate
((accessor field) ...) ((mutator mutable-field) ...))
(begin (define type-name (make-rtd 'type-name 'fields parent))
(define constructor (rtd-constructor type-name))
(define predicate (rtd-predicate type-name))
(define accessor (rtd-accessor type-name 'field))
...
(define mutator (rtd-mutator type-name 'mutable-field))
...)))))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132.sls 0000664 0000000 0000000 00000000663 14737542645 0020027 0 ustar 00root root 0000000 0000000 (library (srfi :132)
(export
list-sorted? vector-sorted? list-merge vector-merge list-sort vector-sort
list-stable-sort vector-stable-sort list-merge! vector-merge! list-sort!
vector-sort! list-stable-sort! vector-stable-sort!
list-delete-neighbor-dups vector-delete-neighbor-dups
list-delete-neighbor-dups! vector-delete-neighbor-dups! vector-find-median
vector-find-median!)
(import (srfi :132 sorting)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/ 0000775 0000000 0000000 00000000000 14737542645 0017277 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/delndups.scm 0000664 0000000 0000000 00000014720 14737542645 0021625 0 ustar 00root root 0000000 0000000 ;;; The sort package -- delete neighboring duplicate elts
;;; Copyright (c) 1998 by Olin Shivers.
;;; This code is open-source; see the end of the file for porting and
;;; more copyright information.
;;; Olin Shivers 11/98.
;;; Problem:
;;; vector-delete-neighbor-dups pushes N stack frames, where N is the number
;;; of elements in the answer vector. This is arguably a very efficient thing
;;; to do, but it might blow out on a system with a limited stack but a big
;;; heap. We could rewrite this to "chunk" up answers in temp vectors if we
;;; push more than a certain number of frames, then allocate a final answer,
;;; copying all the chunks into the answer. But it's much more complex code.
;;; Exports:
;;; (list-delete-neighbor-dups = lis) -> list
;;; (list-delete-neighbor-dups! = lis) -> list
;;; (vector-delete-neighbor-dups = v [start end]) -> vector
;;; (vector-delete-neighbor-dups! = v [start end]) -> end'
;;; These procedures delete adjacent duplicate elements from a list or
;;; a vector, using a given element equality procedure. The first or leftmost
;;; element of a run of equal elements is the one that survives. The list
;;; or vector is not otherwise disordered.
;;;
;;; These procedures are linear time -- much faster than the O(n^2) general
;;; duplicate-elt deletors that do not assume any "bunching" of elements.
;;; If you want to delete duplicate elements from a large list or vector,
;;; sort the elements to bring equal items together, then use one of these
;;; procedures -- for a total time of O(n lg n).
;;; LIST-DELETE-NEIGHBOR-DUPS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Below are multiple versions of the LIST-DELETE-NEIGHBOR-DUPS procedure,
;;; from simple to complex. RECUR's contract: Strip off any leading X's from
;;; LIS, and return that list neighbor-dup-deleted.
;;;
;;; The final version
;;; - shares a common subtail between the input & output list, up to 1024
;;; elements;
;;; - Needs no more than 1024 stack frames.
#;
;;; Simplest version.
;;; - Always allocates a fresh list / never shares storage.
;;; - Needs N stack frames, if answer is length N.
(define (list-delete-neighbor-dups = lis)
(if (pair? lis)
(let ((x0 (car lis)))
(cons x0 (let recur ((x0 x0) (xs (cdr lis)))
(if (pair? xs)
(let ((x1 (car xs))
(x2+ (cdr xs)))
(if (= x0 x1)
(recur x0 x2+) ; Loop, actually.
(cons x1 (recur x1 x2+))))
xs))))
lis))
;;; This version tries to use cons cells from input by sharing longest
;;; common tail between input & output. Still needs N stack frames, for ans
;;; of length N.
(define (list-delete-neighbor-dups = lis)
(if (pair? lis)
(let* ((x0 (car lis))
(xs (cdr lis))
(ans (let recur ((x0 x0) (xs xs))
(if (pair? xs)
(let ((x1 (car xs))
(x2+ (cdr xs)))
(if (= x0 x1)
(recur x0 x2+)
(let ((ans-tail (recur x1 x2+)))
(if (eq? ans-tail x2+) xs
(cons x1 ans-tail)))))
xs))))
(if (eq? ans xs) lis (cons x0 ans)))
lis))
;;; LIST-DELETE-NEIGHBOR-DUPS!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code runs in constant list space, constant stack, and also
;;; does only the minimum SET-CDR!'s necessary.
(define (list-delete-neighbor-dups! = lis)
(if (pair? lis)
(let lp1 ((prev lis) (prev-elt (car lis)) (lis (cdr lis)))
(if (pair? lis)
(let ((lis-elt (car lis))
(next (cdr lis)))
(if (= prev-elt lis-elt)
;; We found the first elts of a run of dups, so we know
;; we're going to have to do a SET-CDR!. Scan to the end of
;; the run, do the SET-CDR!, and loop on LP1.
(let lp2 ((lis next))
(if (pair? lis)
(let ((lis-elt (car lis))
(next (cdr lis)))
(if (= prev-elt lis-elt)
(lp2 next)
(begin (set-cdr! prev lis)
(lp1 lis lis-elt next))))
(set-cdr! prev lis))) ; Ran off end => quit.
(lp1 lis lis-elt next))))))
lis)
(define (vector-delete-neighbor-dups elt= v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(if (< start end)
(let* ((x (vector-ref v start))
(ans (let recur ((x x) (i start) (j 1))
(if (< i end)
(let ((y (vector-ref v i))
(nexti (+ i 1)))
(if (elt= x y)
(recur x nexti j)
(let ((ansvec (recur y nexti (+ j 1))))
(vector-set! ansvec j y)
ansvec)))
(make-vector j)))))
(vector-set! ans 0 x)
ans)
'#()))))
;;; Packs the surviving elements to the left, in range [start,end'),
;;; and returns END'.
(define (vector-delete-neighbor-dups! elt= v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(if (>= start end)
end
;; To eliminate unnecessary copying (read elt i then write the value
;; back at index i), we scan until we find the first dup.
(let skip ((j start) (vj (vector-ref v start)))
(let ((j+1 (+ j 1)))
(if (>= j+1 end)
end
(let ((vj+1 (vector-ref v j+1)))
(if (not (elt= vj vj+1))
(skip j+1 vj+1)
;; OK -- j & j+1 are dups, so we're committed to moving
;; data around. In lp2, v[start,j] is what we've done;
;; v[k,end) is what we have yet to handle.
(let lp2 ((j j) (vj vj) (k (+ j 2)))
(let lp3 ((k k))
(if (>= k end)
(+ j 1) ; Done.
(let ((vk (vector-ref v k))
(k+1 (+ k 1)))
(if (elt= vj vk)
(lp3 k+1)
(let ((j+1 (+ j 1)))
(vector-set! v j+1 vk)
(lp2 j+1 vk k+1))))))))))))))))
;;; Copyright
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code is
;;; Copyright (c) 1998 by Olin Shivers.
;;; The terms are: You may do as you please with this code, as long as
;;; you do not delete this notice or hold me responsible for any outcome
;;; related to its use.
;;;
;;; Blah blah blah. Don't you think source files should contain more lines
;;; of code than copyright notice?
;;;
;;; Code porting
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; If your Scheme has a faster mechanism for handling optional arguments
;;; (e.g., Chez), you should definitely port over to it. Note that argument
;;; defaulting and error-checking are interleaved -- you don't have to
;;; error-check defaulted START/END args to see if they are fixnums that are
;;; legal vector indices for the corresponding vector, etc.
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/lmsort.scm 0000664 0000000 0000000 00000035327 14737542645 0021335 0 ustar 00root root 0000000 0000000 ;;; list merge & list merge-sort -*- Scheme -*-
;;; Copyright (c) 1998 by Olin Shivers.
;;; This code is open-source; see the end of the file for porting and
;;; more copyright information.
;;; Olin Shivers
;;; Exports:
;;; (list-merge < lis lis) -> list
;;; (list-merge! < lis lis) -> list
;;; (list-merge-sort < lis) -> list
;;; (list-merge-sort! < lis) -> list
;;; A stable list merge sort of my own device
;;; Two variants: pure & destructive
;;;
;;; This list merge sort is opportunistic (a "natural" sort) -- it exploits
;;; existing order in the input set. Instead of recursing all the way down to
;;; individual elements, the leaves of the merge tree are maximal contiguous
;;; runs of elements from the input list. So the algorithm does very well on
;;; data that is mostly ordered, with a best-case time of O(n) when the input
;;; list is already completely sorted. In any event, worst-case time is
;;; O(n lg n).
;;;
;;; The destructive variant is "in place," meaning that it allocates no new
;;; cons cells at all; it just rearranges the pairs of the input list with
;;; SET-CDR! to order it.
;;;
;;; The interesting control structure is the combination recursion/iteration
;;; of the core GROW function that does an "opportunistic" DFS walk of the
;;; merge tree, adaptively subdividing in response to the length of the
;;; merges, without requiring any auxiliary data structures beyond the
;;; recursion stack. It's actually quite simple -- ten lines of code.
;;; -Olin Shivers 10/20/98
;;; (mlet ((var-list mv-exp) ...) body ...)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; A LET* form that handles multiple values. Move this into the two clients
;;; if you don't have a module system handy to restrict its visibility...
(define-syntax mlet ; Multiple-value LET*
(syntax-rules ()
((mlet ((() exp) rest ...) body ...)
(begin exp (mlet (rest ...) body ...)))
((mlet (((var) exp) rest ...) body ...)
(let ((var exp)) (mlet (rest ...) body ...)))
((mlet ((vars exp) rest ...) body ...)
(call-with-values (lambda () exp)
(lambda vars (mlet (rest ...) body ...))))
((mlet () body ...) (begin body ...))))
;;; (list-merge-sort < lis)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; A natural, stable list merge sort.
;;; - natural: picks off maximal contiguous runs of pre-ordered data.
;;; - stable: won't invert the order of equal elements in the input list.
(define (list-merge-sort elt< lis)
;; (getrun lis) -> run runlen rest
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Pick a run of non-decreasing data off of non-empty list LIS.
;; Return the length of this run, and the following list.
(define (getrun lis)
(let lp ((ans '()) (i 1) (prev (car lis)) (xs (cdr lis)))
(if (pair? xs)
(let ((x (car xs)))
(if (elt< x prev)
(values (append-reverse ans (cons prev '())) i xs)
(lp (cons prev ans) (+ i 1) x (cdr xs))))
(values (append-reverse ans (cons prev '())) i xs))))
(define (append-reverse rev-head tail)
(let lp ((rev-head rev-head) (tail tail))
(if (null-list? rev-head) tail
(lp (cdr rev-head) (cons (car rev-head) tail)))))
(define (null-list? l)
(cond ((pair? l) #f)
((null? l) #t)
(else (error "argument out of domain" l))))
;; (merge a b) -> list
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; List merge -- stably merge lists A (length > 0) & B (length > 0).
;; This version requires up to |a|+|b| stack frames.
(define (merge a b)
(let recur ((x (car a)) (a a)
(y (car b)) (b b))
(if (elt< y x)
(cons y (let ((b (cdr b)))
(if (pair? b)
(recur x a (car b) b)
a)))
(cons x (let ((a (cdr a)))
(if (pair? a)
(recur (car a) a y b)
b))))))
;; (grow s ls ls2 u lw) -> [a la unused]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The core routine. Read the next 20 lines of comments & all is obvious.
;; - S is a sorted list of length LS > 1.
;; - LS2 is some power of two <= LS.
;; - U is an unsorted list.
;; - LW is a positive integer.
;; Starting with S, and taking data from U as needed, produce
;; a sorted list of *at least* length LW, if there's enough data
;; (LW <= LS + length(U)), or use all of U if not.
;;
;; GROW takes maximal contiguous runs of data from U at a time;
;; it is allowed to return a list *longer* than LW if it gets lucky
;; with a long run.
;;
;; The key idea: If you want a merge operation to "pay for itself," the two
;; lists being merged should be about the same length. Remember that.
;;
;; Returns:
;; - A: The result list
;; - LA: The length of the result list
;; - UNUSED: The unused tail of U.
(define (grow s ls ls2 u lw) ; The core of the sort algorithm.
(if (or (<= lw ls) (not (pair? u))) ; Met quota or out of data?
(values s ls u) ; If so, we're done.
(mlet (((ls2) (let lp ((ls2 ls2))
(let ((ls2*2 (+ ls2 ls2)))
(if (<= ls2*2 ls) (lp ls2*2) ls2))))
;; LS2 is now the largest power of two <= LS.
;; (Just think of it as being roughly LS.)
((r lr u2) (getrun u)) ; Get a run, then
((t lt u3) (grow r lr 1 u2 ls2))) ; grow it up to be T.
(grow (merge s t) (+ ls lt) ; Merge S & T,
(+ ls2 ls2) u3 lw)))) ; and loop.
;; Note: (LENGTH LIS) or any constant guaranteed
;; to be greater can be used in place of INFINITY.
(if (pair? lis) ; Don't sort an empty list.
(mlet (((r lr tail) (getrun lis)) ; Pick off an initial run,
((infinity) #o100000000) ; then grow it up maximally.
((a la v) (grow r lr 1 tail infinity)))
a)
'()))
;;; (list-merge-sort! < lis)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; A natural, stable, destructive, in-place list merge sort.
;;; - natural: picks off maximal contiguous runs of pre-ordered data.
;;; - stable: won't invert the order of equal elements in the input list.
;;; - destructive, in-place: this routine allocates no extra working memory;
;;; it simply rearranges the list with SET-CDR! operations.
(define (list-merge-sort! elt< lis)
;; (getrun lis) -> runlen last rest
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Pick a run of non-decreasing data off of non-empty list LIS.
;; Return the length of this run, the last cons cell of the run,
;; and the following list.
(define (getrun lis)
(let lp ((lis lis) (x (car lis)) (i 1) (next (cdr lis)))
(if (pair? next)
(let ((y (car next)))
(if (elt< y x)
(values i lis next)
(lp next y (+ i 1) (cdr next))))
(values i lis next))))
;; (merge! a enda b endb) -> [m endm]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Destructively and stably merge non-empty lists A & B.
;; The last cons of A is ENDA. (The cdr of ENDA can be non-nil.)
;; the last cons of B is ENDB. (The cdr of ENDB can be non-nil.)
;;
;; Return the first and last cons cells of the merged list.
;; This routine is iterative & in-place: it runs in constant stack and
;; doesn't allocate any cons cells. It is also tedious but simple; don't
;; bother reading it unless necessary.
(define (merge! a enda b endb)
;; The logic of these two loops is completely driven by these invariants:
;; SCAN-A: (CDR PREV) = A. X = (CAR A). Y = (CAR B).
;; SCAN-B: (CDR PREV) = B. X = (CAR A). Y = (CAR B).
(letrec ((scan-a (lambda (prev x a y b) ; Zip down A until we
(cond ((elt< y x) ; find an elt > (CAR B).
(set-cdr! prev b)
(let ((next-b (cdr b)))
(if (eq? b endb)
(begin (set-cdr! b a) enda) ; Done.
(scan-b b x a (car next-b) next-b))))
((eq? a enda) (maybe-set-cdr! a b) endb) ; Done.
(else (let ((next-a (cdr a))) ; Continue scan.
(scan-a a (car next-a) next-a y b))))))
(scan-b (lambda (prev x a y b) ; Zip down B while its
(cond ((elt< y x) ; elts are < (CAR A).
(if (eq? b endb)
(begin (set-cdr! b a) enda) ; Done.
(let ((next-b (cdr b))) ; Continue scan.
(scan-b b x a (car next-b) next-b))))
(else (set-cdr! prev a)
(if (eq? a enda)
(begin (maybe-set-cdr! a b) endb) ; Done.
(let ((next-a (cdr a)))
(scan-a a (car next-a) next-a y b)))))))
;; This guy only writes if he has to. Called at most once.
;; Pointer equality rules; pure languages are for momma's boys.
(maybe-set-cdr! (lambda (pair val) (if (not (eq? (cdr pair) val))
(set-cdr! pair val)))))
(let ((x (car a)) (y (car b)))
(if (elt< y x)
;; B starts the answer list.
(values b (if (eq? b endb)
(begin (set-cdr! b a) enda)
(let ((next-b (cdr b)))
(scan-b b x a (car next-b) next-b))))
;; A starts the answer list.
(values a (if (eq? a enda)
(begin (maybe-set-cdr! a b) endb)
(let ((next-a (cdr a)))
(scan-a a (car next-a) next-a y b))))))))
;; (grow s ends ls ls2 u lw) -> [a enda la unused]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The core routine.
;; - S is a sorted list of length LS > 1, with final cons cell ENDS.
;; (CDR ENDS) doesn't have to be nil.
;; - LS2 is some power of two <= LS.
;; - U is an unsorted list.
;; - LW is a positive integer.
;; Starting with S, and taking data from U as needed, produce
;; a sorted list of *at least* length LW, if there's enough data
;; (LW <= LS + length(U)), or use all of U if not.
;;
;; GROW takes maximal contiguous runs of data from U at a time;
;; it is allowed to return a list *longer* than LW if it gets lucky
;; with a long run.
;;
;; The key idea: If you want a merge operation to "pay for itself," the two
;; lists being merged should be about the same length. Remember that.
;;
;; Returns:
;; - A: The result list (not properly terminated)
;; - ENDA: The last cons cell of the result list.
;; - LA: The length of the result list
;; - UNUSED: The unused tail of U.
(define (grow s ends ls ls2 u lw)
(if (and (pair? u) (< ls lw))
;; We haven't met the LW quota but there's still some U data to use.
(mlet (((ls2) (let lp ((ls2 ls2))
(let ((ls2*2 (+ ls2 ls2)))
(if (<= ls2*2 ls) (lp ls2*2) ls2))))
;; LS2 is now the largest power of two <= LS.
;; (Just think of it as being roughly LS.)
((lr endr u2) (getrun u)) ; Get a run from U;
((t endt lt u3) (grow u endr lr 1 u2 ls2)) ; grow it up to be T.
((st end-st) (merge! s ends t endt))) ; Merge S & T,
(grow st end-st (+ ls lt) (+ ls2 ls2) u3 lw)) ; then loop.
(values s ends ls u))) ; Done -- met LW quota or ran out of data.
;; Note: (LENGTH LIS) or any constant guaranteed
;; to be greater can be used in place of INFINITY.
(if (pair? lis)
(mlet (((lr endr rest) (getrun lis)) ; Pick off an initial run.
((infinity) #o100000000) ; Then grow it up maximally.
((a enda la v) (grow lis endr lr 1 rest infinity)))
(set-cdr! enda '()) ; Nil-terminate answer.
a) ; We're done.
'())) ; Don't sort an empty list.
;;; Merge
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; These two merge procedures are stable -- ties favor list A.
(define (list-merge < a b)
(cond ((not (pair? a)) b)
((not (pair? b)) a)
(else (let recur ((x (car a)) (a a) ; A is a pair; X = (CAR A).
(y (car b)) (b b)) ; B is a pair; Y = (CAR B).
(if (< y x)
(let ((b (cdr b)))
(if (pair? b)
(cons y (recur x a (car b) b))
(cons y a)))
(let ((a (cdr a)))
(if (pair? a)
(cons x (recur (car a) a y b))
(cons x b))))))))
;;; This destructive merge does as few SET-CDR!s as it can -- for example, if
;;; the list is already sorted, it does no SET-CDR!s at all. It is also
;;; iterative, running in constant stack.
(define (list-merge! < a b)
;; The logic of these two loops is completely driven by these invariants:
;; SCAN-A: (CDR PREV) = A. X = (CAR A). Y = (CAR B).
;; SCAN-B: (CDR PREV) = B. X = (CAR A). Y = (CAR B).
(letrec ((scan-a (lambda (prev a x b y) ; Zip down A doing
(if (< y x) ; no SET-CDR!s until
(let ((next-b (cdr b))) ; we hit a B elt that
(set-cdr! prev b) ; has to be inserted.
(if (pair? next-b)
(scan-b b a x next-b (car next-b))
(set-cdr! b a)))
(let ((next-a (cdr a)))
(if (pair? next-a)
(scan-a a next-a (car next-a) b y)
(set-cdr! a b))))))
(scan-b (lambda (prev a x b y) ; Zip down B doing
(if (< y x) ; no SET-CDR!s until
(let ((next-b (cdr b))) ; we hit an A elt that
(if (pair? next-b) ; has to be
(scan-b b a x next-b (car next-b)) ; inserted.
(set-cdr! b a)))
(let ((next-a (cdr a)))
(set-cdr! prev a)
(if (pair? next-a)
(scan-a a next-a (car next-a) b y)
(set-cdr! a b)))))))
(cond ((not (pair? a)) b)
((not (pair? b)) a)
;; B starts the answer list.
((< (car b) (car a))
(let ((next-b (cdr b)))
(if (null? next-b)
(set-cdr! b a)
(scan-b b a (car a) next-b (car next-b))))
b)
;; A starts the answer list.
(else (let ((next-a (cdr a)))
(if (null? next-a)
(set-cdr! a b)
(scan-a a next-a (car next-a) b (car b))))
a))))
;;; Copyright
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code is
;;; Copyright (c) 1998 by Olin Shivers.
;;; The terms are: You may do as you please with this code, as long as
;;; you do not delete this notice or hold me responsible for any outcome
;;; related to its use.
;;;
;;; Blah blah blah.
;;; Code tuning & porting
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This is very portable code. It's R4RS with the following exceptions:
;;; - The R5RS multiple-value VALUES & CALL-WITH-VALUES procedures for
;;; handling multiple-value return.
;;;
;;; This code is *tightly* bummed as far as I can go in portable Scheme.
;;;
;;; - The fixnum arithmetic in LIST-MERGE-SORT! and COUNTED-LIST-MERGE!
;;; that could be safely switched over to unsafe, fixnum-specific ops,
;;; if you're sure that 2*maxlen is a fixnum, where maxlen is the length
;;; of the longest list you could ever have.
;;;
;;; - I typically write my code in a style such that every CAR and CDR
;;; application is protected by an upstream PAIR?. This is the case in this
;;; code, so all the CAR's and CDR's could safely switched over to unsafe
;;; versions. But check over the code before you do it, in case the source
;;; has been altered since I wrote this.
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/median.scm 0000664 0000000 0000000 00000001721 14737542645 0021241 0 ustar 00root root 0000000 0000000 ;;;; Finding the median of a vector
;; This involves sorting the vector, which is why it's part
;; of this package.
(define (vector-find-median < v knil . maybe-mean)
(define mean (if (null? maybe-mean)
(lambda (a b) (/ (+ a b) 2))
(car maybe-mean)))
(define len (vector-length v))
(define newv (vector-sort < v))
(cond
((= len 0) knil)
((odd? len) (vector-ref newv (/ (- len 1) 2)))
(else (mean
(vector-ref newv (- (/ len 2) 1))
(vector-ref newv (/ len 2))))))
(define (vector-find-median! < v knil . maybe-mean)
(define mean (if (null? maybe-mean)
(lambda (a b) (/ (+ a b) 2))
(car maybe-mean)))
(define len (vector-length v))
(define newv (vector-sort! < v))
(cond
((= len 0) knil)
((odd? len) (vector-ref newv (/ (- len 1) 2)))
(else (mean
(vector-ref newv (- (/ len 2) 1))
(vector-ref newv (/ len 2))))))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/merge.scm 0000664 0000000 0000000 00000017461 14737542645 0021113 0 ustar 00root root 0000000 0000000 ;;; This file extracts four merge procedures from lmsort.scm and vmsort.scm
;;; files written by Olin Shivers.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Start of code extracted from Olin's lmsort.scm file.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; list merge & list merge-sort -*- Scheme -*-
;;; Copyright (c) 1998 by Olin Shivers.
;;; This code is open-source; see the end of the file for porting and
;;; more copyright information.
;;; Olin Shivers
;;; Exports:
;;; (list-merge < lis lis) -> list
;;; (list-merge! < lis lis) -> list
;;; Merge
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; These two merge procedures are stable -- ties favor list A.
(define (list-merge < a b)
(cond ((not (pair? a)) b)
((not (pair? b)) a)
(else (let recur ((x (car a)) (a a) ; A is a pair; X = (CAR A).
(y (car b)) (b b)) ; B is a pair; Y = (CAR B).
(if (< y x)
(let ((b (cdr b)))
(if (pair? b)
(cons y (recur x a (car b) b))
(cons y a)))
(let ((a (cdr a)))
(if (pair? a)
(cons x (recur (car a) a y b))
(cons x b))))))))
;;; This destructive merge does as few SET-CDR!s as it can -- for example, if
;;; the list is already sorted, it does no SET-CDR!s at all. It is also
;;; iterative, running in constant stack.
(define (list-merge! < a b)
;; The logic of these two loops is completely driven by these invariants:
;; SCAN-A: (CDR PREV) = A. X = (CAR A). Y = (CAR B).
;; SCAN-B: (CDR PREV) = B. X = (CAR A). Y = (CAR B).
(letrec ((scan-a (lambda (prev a x b y) ; Zip down A doing
(if (< y x) ; no SET-CDR!s until
(let ((next-b (cdr b))) ; we hit a B elt that
(set-cdr! prev b) ; has to be inserted.
(if (pair? next-b)
(scan-b b a x next-b (car next-b))
(set-cdr! b a)))
(let ((next-a (cdr a)))
(if (pair? next-a)
(scan-a a next-a (car next-a) b y)
(set-cdr! a b))))))
(scan-b (lambda (prev a x b y) ; Zip down B doing
(if (< y x) ; no SET-CDR!s until
(let ((next-b (cdr b))) ; we hit an A elt that
(if (pair? next-b) ; has to be
(scan-b b a x next-b (car next-b)) ; inserted.
(set-cdr! b a)))
(let ((next-a (cdr a)))
(set-cdr! prev a)
(if (pair? next-a)
(scan-a a next-a (car next-a) b y)
(set-cdr! a b)))))))
(cond ((not (pair? a)) b)
((not (pair? b)) a)
;; B starts the answer list.
((< (car b) (car a))
(let ((next-b (cdr b)))
(if (null? next-b)
(set-cdr! b a)
(scan-b b a (car a) next-b (car next-b))))
b)
;; A starts the answer list.
(else (let ((next-a (cdr a)))
(if (null? next-a)
(set-cdr! a b)
(scan-a a next-a (car next-a) b (car b))))
a))))
;;; Copyright
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code is
;;; Copyright (c) 1998 by Olin Shivers.
;;; The terms are: You may do as you please with this code, as long as
;;; you do not delete this notice or hold me responsible for any outcome
;;; related to its use.
;;;
;;; Blah blah blah.
;;; Code tuning & porting
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This is very portable code. It's R4RS with the following exceptions:
;;; - The R5RS multiple-value VALUES & CALL-WITH-VALUES procedures for
;;; handling multiple-value return.
;;;
;;; This code is *tightly* bummed as far as I can go in portable Scheme.
;;;
;;; - The fixnum arithmetic in LIST-MERGE-SORT! and COUNTED-LIST-MERGE!
;;; that could be safely switched over to unsafe, fixnum-specific ops,
;;; if you're sure that 2*maxlen is a fixnum, where maxlen is the length
;;; of the longest list you could ever have.
;;;
;;; - I typically write my code in a style such that every CAR and CDR
;;; application is protected by an upstream PAIR?. This is the case in this
;;; code, so all the CAR's and CDR's could safely switched over to unsafe
;;; versions. But check over the code before you do it, in case the source
;;; has been altered since I wrote this.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; End of code extracted from Olin's lmsort.scm file.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Start of code extracted from Olin's vmsort.scm file.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The sort package -- stable vector merge & merge sort -*- Scheme -*-
;;; Copyright (c) 1998 by Olin Shivers.
;;; This code is open-source; see the end of the file for porting and
;;; more copyright information.
;;; Olin Shivers 10/98.
;;; Exports:
;;; (vector-merge < v1 v2 [start1 end1 start2 end2]) -> vector
;;; (vector-merge! < v v1 v2 [start0 start1 end1 start2 end2]) -> unspecific
;;;
;;; (vector-merge-sort < v [start end temp]) -> vector
;;; (vector-merge-sort! < v [start end temp]) -> unspecific
;;; Merge
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; (vector-merge < v1 v2 [start1 end1 start2 end2]) -> vector
;;; (vector-merge! < v v1 v2 [start start1 end1 start2 end2]) -> unspecific
;;;
;;; Stable vector merge -- V1's elements come out ahead of equal V2 elements.
(define (vector-merge < v1 v2 . maybe-starts+ends)
(call-with-values
(lambda () (vectors-start+end-2 v1 v2 maybe-starts+ends))
(lambda (start1 end1 start2 end2)
(let ((ans (make-vector (+ (- end1 start1) (- end2 start2)))))
(%vector-merge! < ans v1 v2 0 start1 end1 start2 end2)
ans))))
(define (vector-merge! < v v1 v2 . maybe-starts+ends)
(call-with-values
(lambda ()
(if (pair? maybe-starts+ends)
(values (car maybe-starts+ends)
(cdr maybe-starts+ends))
(values 0
'())))
(lambda (start rest)
(call-with-values
(lambda () (vectors-start+end-2 v1 v2 rest))
(lambda (start1 end1 start2 end2)
(%vector-merge! < v v1 v2 start start1 end1 start2 end2))))))
;;; This routine is not exported. The code is tightly bummed.
;;;
;;; If these preconditions hold, the routine can be bummed to run with
;;; unsafe vector-indexing and fixnum arithmetic ops:
;;; - V V1 V2 are vectors.
;;; - START START1 END1 START2 END2 are fixnums.
;;; - (<= 0 START END0 (vector-length V),
;;; where end0 = start + (end1 - start1) + (end2 - start2)
;;; - (<= 0 START1 END1 (vector-length V1))
;;; - (<= 0 START2 END2 (vector-length V2))
;;; If you put these error checks in the two client procedures above, you can
;;; safely convert this procedure to use unsafe ops -- which is why it isn't
;;; exported. This will provide *huge* speedup.
(define (%vector-merge! elt< v v1 v2 start start1 end1 start2 end2)
(letrec ((vblit (lambda (fromv j i end) ; Blit FROMV[J,END) to V[I,?].
(let lp ((j j) (i i))
(vector-set! v i (vector-ref fromv j))
(let ((j (+ j 1)))
(if (< j end) (lp j (+ i 1))))))))
(cond ((<= end1 start1) (if (< start2 end2) (vblit v2 start2 start end2)))
((<= end2 start2) (vblit v1 start1 start end1))
;; Invariants: I is next index of V to write; X = V1[J]; Y = V2[K].
(else (let lp ((i start)
(j start1) (x (vector-ref v1 start1))
(k start2) (y (vector-ref v2 start2)))
(let ((i1 (+ i 1))) ; "i+1" is a complex number in R4RS!
(if (elt< y x)
(let ((k (+ k 1)))
(vector-set! v i y)
(if (< k end2)
(lp i1 j x k (vector-ref v2 k))
(vblit v1 j i1 end1)))
(let ((j (+ j 1)))
(vector-set! v i x)
(if (< j end1)
(lp i1 j (vector-ref v1 j) k y)
(vblit v2 k i1 end2))))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; End of code extracted from Olin's vmsort.scm file.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/select.scm 0000664 0000000 0000000 00000024003 14737542645 0021261 0 ustar 00root root 0000000 0000000 ;;; Linear-time (average case) algorithms for:
;;;
;;; Selecting the kth smallest element from an unsorted vector.
;;; Selecting the kth and (k+1)st smallest elements from an unsorted vector.
;;; Selecting the median from an unsorted vector.
;;; These procedures are part of SRFI 132 but are missing from
;;; its reference implementation as of 10 March 2016.
;;; SRFI 132 says this procedure runs in O(n) time.
;;; As implemented, however, the worst-case time is O(n^2) because
;;; vector-select is implemented using randomized quickselect.
;;; The average time is O(n), and you'd have to be unlucky
;;; to approach the worst case.
(define (vector-find-median < v knil . rest)
(let* ((mean (if (null? rest)
(lambda (a b) (/ (+ a b) 2))
(car rest)))
(n (vector-length v)))
(cond ((zero? n)
knil)
((odd? n)
(%vector-select < v (quotient n 2) 0 n))
(else
(call-with-values
(lambda () (%vector-select2 < v (- (quotient n 2) 1) 0 n))
(lambda (a b)
(mean a b)))))))
;;; For this procedure, the SRFI 132 specification
;;; demands the vector be sorted (by side effect).
(define (vector-find-median! < v knil . rest)
(let* ((mean (if (null? rest)
(lambda (a b) (/ (+ a b) 2))
(car rest)))
(n (vector-length v)))
(vector-sort! < v)
(cond ((zero? n)
knil)
((odd? n)
(vector-ref v (quotient n 2)))
(else
(mean (vector-ref v (- (quotient n 2) 1))
(vector-ref v (quotient n 2)))))))
;;; SRFI 132 says this procedure runs in O(n) time.
;;; As implemented, however, the worst-case time is O(n^2).
;;; The average time is O(n), and you'd have to be unlucky
;;; to approach the worst case.
;;;
;;; After rest argument processing, calls the private version defined below.
(define (vector-select < v k . rest)
(let* ((start (if (null? rest)
0
(car rest)))
(end (if (and (pair? rest)
(pair? (cdr rest)))
(car (cdr rest))
(vector-length v))))
(%vector-select < v k start end)))
;;; The vector-select procedure is needed internally to implement
;;; vector-find-median, but SRFI 132 has been changed (for no good
;;; reason) to export vector-select! instead of vector-select.
;;; Fortunately, vector-select! is not required to have side effects.
(define vector-select! vector-select)
;;; This could be made slightly more efficient, but who cares?
(define (vector-separate! < v k . rest)
(let* ((start (if (null? rest)
0
(car rest)))
(end (if (and (pair? rest)
(pair? (cdr rest)))
(car (cdr rest))
(vector-length v))))
(if (and (> k 0)
(> end start))
(let ((pivot (vector-select < v (- k 1) start end)))
(call-with-values
(lambda () (count-smaller < pivot v start end 0 0))
(lambda (count count2)
(let* ((v2 (make-vector count))
(v3 (make-vector (- end start count count2))))
(copy-smaller! < pivot v2 0 v start end)
(copy-bigger! < pivot v3 0 v start end)
(r7rs-vector-copy! v start v2)
(r7rs-vector-fill! v pivot (+ start count) (+ start count count2))
(r7rs-vector-copy! v (+ start count count2) v3))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; For small ranges, sorting may be the fastest way to find the kth element.
;;; This threshold is not at all critical, and may not even be worthwhile.
(define just-sort-it-threshold 50)
;;; Given
;;; an irreflexive total order
;;; a vector v
;;; an index k
;;; an index start
;;; an index end
;;; with
;;; 0 <= k < (- end start)
;;; 0 <= start < end <= (vector-length v)
;;; returns
;;; (vector-ref (vector-sort (vector-copy v start end)) (+ start k))
;;; but is usually faster than that.
(define (%vector-select v k start end)
(assert (and 'vector-select
(procedure? )
(vector? v)
(exact-integer? k)
(exact-integer? start)
(exact-integer? end)
(<= 0 k (- end start 1))
(<= 0 start end (vector-length v))))
(%%vector-select v k start end))
;;; Given
;;; an irreflexive total order
;;; a vector v
;;; an index k
;;; an index start
;;; an index end
;;; with
;;; 0 <= k < (- end start 1)
;;; 0 <= start < end <= (vector-length v)
;;; returns two values:
;;; (vector-ref (vector-sort (vector-copy v start end)) (+ start k))
;;; (vector-ref (vector-sort (vector-copy v start end)) (+ start k 1))
;;; but is usually faster than that.
(define (%vector-select2 v k start end)
(assert (and 'vector-select
(procedure? )
(vector? v)
(exact-integer? k)
(exact-integer? start)
(exact-integer? end)
(<= 0 k (- end start 1 1))
(<= 0 start end (vector-length v))))
(%%vector-select2 v k start end))
;;; Like %vector-select, but its preconditions have been checked.
(define (%%vector-select v k start end)
(let ((size (- end start)))
(cond ((= 1 size)
(vector-ref v (+ k start)))
((= 2 size)
(cond (( (vector-ref v start)
(vector-ref v (+ start 1)))
(vector-ref v (+ k start)))
(else
(vector-ref v (+ (- 1 k) start)))))
((< size just-sort-it-threshold)
(vector-ref (vector-sort (r7rs-vector-copy v start end)) k))
(else
(let* ((ip (random-integer size))
(pivot (vector-ref v (+ start ip))))
(call-with-values
(lambda () (count-smaller pivot v start end 0 0))
(lambda (count count2)
(cond ((< k count)
(let* ((n count)
(v2 (make-vector n)))
(copy-smaller! pivot v2 0 v start end)
(%%vector-select v2 k 0 n)))
((< k (+ count count2))
pivot)
(else
(let* ((n (- size count count2))
(v2 (make-vector n))
(k2 (- k count count2)))
(copy-bigger! pivot v2 0 v start end)
(%%vector-select v2 k2 0 n)))))))))))
;;; Like %%vector-select, but returns two values:
;;;
;;; (vector-ref (vector-sort (vector-copy v start end)) (+ start k))
;;; (vector-ref (vector-sort (vector-copy v start end)) (+ start k 1))
;;;
;;; Returning two values is useful when finding the median of an even
;;; number of things.
(define (%%vector-select2 v k start end)
(let ((size (- end start)))
(cond ((= 2 size)
(let ((a (vector-ref v start))
(b (vector-ref v (+ start 1))))
(cond (( a b)
(values a b))
(else
(values b a)))))
((< size just-sort-it-threshold)
(let ((v2 (vector-sort (r7rs-vector-copy v start end))))
(values (vector-ref v2 k)
(vector-ref v2 (+ k 1)))))
(else
(let* ((ip (random-integer size))
(pivot (vector-ref v (+ start ip))))
(call-with-values
(lambda () (count-smaller pivot v start end 0 0))
(lambda (count count2)
(cond ((= (+ k 1) count)
(values (%%vector-select v k start end)
pivot))
((< k count)
(let* ((n count)
(v2 (make-vector n)))
(copy-smaller! pivot v2 0 v start end)
(%%vector-select2 v2 k 0 n)))
((< k (+ count count2))
(values pivot
(if (< (+ k 1) (+ count count2))
pivot
(%%vector-select v (+ k 1) start end))))
(else
(let* ((n (- size count count2))
(v2 (make-vector n))
(k2 (- k count count2)))
(copy-bigger! pivot v2 0 v start end)
(%%vector-select2 v2 k2 0 n)))))))))))
;;; Counts how many elements within the range are less than the pivot
;;; and how many are equal to the pivot, returning both of those counts.
(define (count-smaller pivot v i end count count2)
(cond ((= i end)
(values count count2))
(( (vector-ref v i) pivot)
(count-smaller pivot v (+ i 1) end (+ count 1) count2))
(( pivot (vector-ref v i))
(count-smaller pivot v (+ i 1) end count count2))
(else
(count-smaller pivot v (+ i 1) end count (+ count2 1)))))
;;; Like vector-copy! but copies an element only if it is less than the pivot.
;;; The destination vector must be large enough.
(define (copy-smaller! pivot dst at src start end)
(cond ((= start end) dst)
(( (vector-ref src start) pivot)
(vector-set! dst at (vector-ref src start))
(copy-smaller! pivot dst (+ at 1) src (+ start 1) end))
(else
(copy-smaller! pivot dst at src (+ start 1) end))))
;;; Like copy-smaller! but copies only elements that are greater than the pivot.
(define (copy-bigger! pivot dst at src start end)
(cond ((= start end) dst)
(( pivot (vector-ref src start))
(vector-set! dst at (vector-ref src start))
(copy-bigger! pivot dst (+ at 1) src (+ start 1) end))
(else
(copy-bigger! pivot dst at src (+ start 1) end))))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/sort.scm 0000664 0000000 0000000 00000001512 14737542645 0020771 0 ustar 00root root 0000000 0000000 ;;; The sort package -- general sort & merge procedures
;;;
;;; Copyright (c) 1998 by Olin Shivers.
;;; You may do as you please with this code, as long as you do not delete this
;;; notice or hold me responsible for any outcome related to its use.
;;; Olin Shivers 10/98.
;;; This file just defines the general sort API in terms of some
;;; algorithm-specific calls.
(define (list-sort < l) ; Sort lists by converting to
(let ((v (list->vector l))) ; a vector and sorting that.
(vector-heap-sort! < v)
(vector->list v)))
(define list-sort! list-merge-sort!)
(define list-stable-sort list-merge-sort)
(define list-stable-sort! list-merge-sort!)
(define vector-sort vector-quick-sort)
(define vector-sort! vector-quick-sort!)
(define vector-stable-sort vector-merge-sort)
(define vector-stable-sort! vector-merge-sort!)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/sortfaster.scm 0000664 0000000 0000000 00000003145 14737542645 0022202 0 ustar 00root root 0000000 0000000 ;;; SRFI 132 specifies these eight procedures.
;;;
;;; Benchmarking has shown that the (rnrs sorting) procedures
;;; are faster than the sorting procedures defined by SRFI 132's
;;; reference implementation, so the R6RS procedures are used here.
;;;
;;; This file is a plug-and-play alternative to sort.scm in the
;;; same directory.
(define list-sort r6rs-list-sort)
(define list-sort! r6rs-list-sort)
(define list-stable-sort r6rs-list-sort)
(define list-stable-sort! r6rs-list-sort)
(define (vector-sort < v . rest)
(cond ((null? rest)
(r6rs-vector-sort < v))
((null? (cdr rest))
(r6rs-vector-sort < (r7rs-vector-copy v (car rest))))
((null? (cddr rest))
(r6rs-vector-sort < (r7rs-vector-copy v (car rest) (cadr rest))))
(else
(error 'vector-sort
"too many arguments"
(cons < (cons v rest))))))
(define vector-stable-sort vector-sort)
(define (vector-sort! < v . rest)
(cond ((null? rest)
(r6rs-vector-sort! < v))
((null? (cdr rest))
(let* ((start (car rest))
(v2 (r7rs-vector-copy v start)))
(r6rs-vector-sort! < v2)
(r7rs-vector-copy! v start v2 0)))
((null? (cddr rest))
(let* ((start (car rest))
(end (cadr rest))
(v2 (r7rs-vector-copy v start end)))
(r6rs-vector-sort! < v2)
(r7rs-vector-copy! v start v2 0)))
(else
(error 'vector-sort!
"too many arguments"
(cons < (cons v rest))))))
(define vector-stable-sort! vector-sort!)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/sorting-test.scm 0000664 0000000 0000000 00000004714 14737542645 0022453 0 ustar 00root root 0000000 0000000 ;;; Little test harness, 'cause I'm paraoid about tricky code.
;;; This code is
;;; Copyright (c) 1998 by Olin Shivers.
;;; The terms are: You may do as you please with this code, as long as
;;; you do not delete this notice or hold me responsible for any outcome
;;; related to its use.
;;;
;;; Blah blah blah. Don't you think source files should contain more lines
;;; of code than copyright notice?
(define-test-suite sort-tests)
;; Three-way comparison for numbers
(define (my-c x y)
(cond ((= x y) 0)
((< x y) -1)
(else 1)))
;;; For testing stable sort -- 3 & -3 compare the same.
(define (my< x y) (< (abs x) (abs y)))
(define (unstable-sort-test v) ; quick & heap vs simple insert
(let ((v1 (vector-copy v))
(v2 (vector-copy v))
(v3 (vector-copy v))
(v4 (vector-copy v)))
(vector-heap-sort! < v1)
(vector-insert-sort! < v2)
(vector-quick-sort! < v3)
(vector-quick-sort3! my-c v4)
(check-that v2 (is v1))
(check-that v3 (is v1))
(check-that v4 (is v1))
(check-that v1 (is (lambda (v) (vector-sorted? < v))))))
(define (stable-sort-test v) ; insert, list & vector merge sorts
(let ((v1 (vector-copy v))
(v2 (vector-copy v))
(v3 (list->vector (list-merge-sort! my< (vector->list v))))
(v4 (list->vector (list-merge-sort my< (vector->list v)))))
(vector-merge-sort! my< v1)
(vector-insert-sort! my< v2)
(check-that v1 (is (lambda (v) (vector-sorted? my< v))))
(check-that v2 (is v1))
(check-that v3 (is v1))
(check-that v4 (is v1))))
(define (run-sort-test sort-test count max-size)
(let loop ((i 0))
(if (< i count)
(begin
(sort-test (random-vector (random-integer max-size)))
(loop (+ 1 i))))))
(define-test-case stable-sort sort-tests
(run-sort-test stable-sort-test 10 4096))
(define-test-case unstable-sort sort-tests
(run-sort-test unstable-sort-test 10 4096))
(define (random-vector size)
(let ((v (make-vector size)))
(fill-vector-randomly! v (* 10 size))
v))
(define (fill-vector-randomly! v range)
(let ((half (quotient range 2)))
(do ((i (- (vector-length v) 1) (- i 1)))
((< i 0))
(vector-set! v i (- (random-integer range) half)))))
(define (vector-portion-copy vec start end)
(let* ((len (vector-length vec))
(new-len (- end start))
(new (make-vector new-len)))
(do ((i start (+ i 1))
(j 0 (+ j 1)))
((= i end) new)
(vector-set! new j (vector-ref vec i)))))
(define (vector-copy vec)
(vector-portion-copy vec 0 (vector-length vec)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/sorting.sls 0000664 0000000 0000000 00000002377 14737542645 0021520 0 ustar 00root root 0000000 0000000 (library (srfi :132 sorting)
(export
list-sorted? vector-sorted? list-merge vector-merge list-sort vector-sort
list-stable-sort vector-stable-sort list-merge! vector-merge! list-sort!
vector-sort! list-stable-sort! vector-stable-sort!
list-delete-neighbor-dups vector-delete-neighbor-dups
list-delete-neighbor-dups! vector-delete-neighbor-dups! vector-find-median
vector-find-median!)
(import (except (rnrs) list-sort vector-sort vector-sort!)
(rnrs mutable-pairs)
(rename (only (srfi :133 vectors) vector-copy! vector-copy)
(vector-copy! r7rs-vector-copy!)
(vector-copy r7rs-vector-copy))
(only (rnrs r5rs) quotient)
(srfi private include))
(include/resolve ("srfi" "%3a132") "delndups.scm")
(include/resolve ("srfi" "%3a132") "lmsort.scm")
(include/resolve ("srfi" "%3a132") "sortp.scm")
(include/resolve ("srfi" "%3a132") "vector-util.scm")
(include/resolve ("srfi" "%3a132") "vhsort.scm")
(include/resolve ("srfi" "%3a132") "visort.scm")
(include/resolve ("srfi" "%3a132") "vmsort.scm")
(include/resolve ("srfi" "%3a132") "vqsort2.scm")
(include/resolve ("srfi" "%3a132") "median.scm")
(include/resolve ("srfi" "%3a132") "sort.scm") ; must be last
)
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/sortp.scm 0000664 0000000 0000000 00000002460 14737542645 0021154 0 ustar 00root root 0000000 0000000 ;;; The sort package -- sorted predicates
;;; Olin Shivers 10/98.
;;;
;;; (list-sorted? < lis) -> boolean
;;; (vector-sorted? < v [start end]) -> boolean
(define (list-sorted? < list)
(or (not (pair? list))
(let lp ((prev (car list)) (tail (cdr list)))
(or (not (pair? tail))
(let ((next (car tail)))
(and (not (< next prev))
(lp next (cdr tail))))))))
(define (vector-sorted? elt< v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(or (>= start end) ; Empty range
(let lp ((i (+ start 1)) (vi-1 (vector-ref v start)))
(or (>= i end)
(let ((vi (vector-ref v i)))
(and (not (elt< vi vi-1))
(lp (+ i 1) vi)))))))))
;;; Copyright and porting non-notices
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Give me a break. It's fifteen lines of code. I place this code in the
;;; public domain; help yourself.
;;;
;;; If your Scheme has a faster mechanism for handling optional arguments
;;; (e.g., Chez), you should definitely port over to it. Note that argument
;;; defaulting and error-checking are interleaved -- you don't have to
;;; error-check defaulted START/END args to see if they are fixnums that are
;;; legal vector indices for the corresponding vector, etc.
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/vbinsearch.scm 0000664 0000000 0000000 00000002336 14737542645 0022133 0 ustar 00root root 0000000 0000000 ;;; The sort package -- binary search -*- Scheme -*-
;;; Copyright (c) 1998 by Olin Shivers.
;;; This code is in the public domain.
;;; Olin Shivers 98/11
;;; Returns the index of the matching element.
;;; (vector-binary-search < car 4 '#((1 . one) (3 . three)
;;; (4 . four) (25 . twenty-five)))
;;; => 2
(define (vector-binary-search key< elt->key key v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(let lp ((left start) (right end)) ; Search V[left,right).
(and (< left right)
(let* ((m (quotient (+ left right) 2))
(elt (vector-ref v m))
(elt-key (elt->key elt)))
(cond ((key< key elt-key) (lp left m))
((key< elt-key key) (lp (+ m 1) right))
(else m))))))))
(define (vector-binary-search3 compare v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(let lp ((left start) (right end)) ; Search V[left,right).
(and (< left right)
(let* ((m (quotient (+ left right) 2))
(sign (compare (vector-ref v m))))
(cond ((> sign 0) (lp left m))
((< sign 0) (lp (+ m 1) right))
(else m))))))))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/vector-util.scm 0000664 0000000 0000000 00000003530 14737542645 0022261 0 ustar 00root root 0000000 0000000 ;;; This code is
;;; Copyright (c) 1998 by Olin Shivers.
;;; The terms are: You may do as you please with this code, as long as
;;; you do not delete this notice or hold me responsible for any outcome
;;; related to its use.
;;;
;;; Blah blah blah. Don't you think source files should contain more lines
;;; of code than copyright notice?
(define (vector-portion-copy vec start end)
(let* ((len (vector-length vec))
(new-len (- end start))
(new (make-vector new-len)))
(do ((i start (+ i 1))
(j 0 (+ j 1)))
((= i end) new)
(vector-set! new j (vector-ref vec i)))))
(define (vector-copy vec)
(vector-portion-copy vec 0 (vector-length vec)))
(define (vector-portion-copy! target src start end)
(let ((len (- end start)))
(do ((i (- len 1) (- i 1))
(j (- end 1) (- j 1)))
((< i 0))
(vector-set! target i (vector-ref src j)))))
(define (has-element list index)
(cond
((zero? index)
(if (pair? list)
(values #t (car list))
(values #f #f)))
((null? list)
(values #f #f))
(else
(has-element (cdr list) (- index 1)))))
(define (list-ref-or-default list index default)
(call-with-values
(lambda () (has-element list index))
(lambda (has? maybe)
(if has?
maybe
default))))
(define (vector-start+end vector maybe-start+end)
(let ((start (list-ref-or-default maybe-start+end
0 0))
(end (list-ref-or-default maybe-start+end
1 (vector-length vector))))
(values start end)))
(define (vectors-start+end-2 vector-1 vector-2 maybe-start+end)
(let ((start-1 (list-ref-or-default maybe-start+end
0 0))
(end-1 (list-ref-or-default maybe-start+end
1 (vector-length vector-1)))
(start-2 (list-ref-or-default maybe-start+end
2 0))
(end-2 (list-ref-or-default maybe-start+end
3 (vector-length vector-2))))
(values start-1 end-1
start-2 end-2)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/vhsort.scm 0000664 0000000 0000000 00000012115 14737542645 0021330 0 ustar 00root root 0000000 0000000 ;;; The sort package -- vector heap sort -*- Scheme -*-
;;; Copyright (c) 2002 by Olin Shivers.
;;; This code is open-source; see the end of the file for porting and
;;; more copyright information.
;;; Olin Shivers 10/98.
;;; Exports:
;;; (vector-heap-sort! elt< v [start end]) -> unspecified
;;; (vector-heap-sort elt< v [start end]) -> vector
;;; Two key facts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; If a heap structure is embedded into a vector at indices [start,end), then:
;;; 1. The two children of index k are start + 2*(k-start) + 1 = k*2-start+1
;;; and start + 2*(k-start) + 2 = k*2-start+2.
;;;
;;; 2. The first index of a leaf node in the range [start,end) is
;;; first-leaf = floor[(start+end)/2]
;;; (You can deduce this from fact #1 above.)
;;; Any index before FIRST-LEAF is an internal node.
(define (really-vector-heap-sort! elt< v start end)
;; Vector V contains a heap at indices [START,END). The heap is in heap
;; order in the range (I,END) -- i.e., every element in this range is >=
;; its children. Bubble HEAP[I] down into the heap to impose heap order on
;; the range [I,END).
(define (restore-heap! end i)
(let* ((vi (vector-ref v i))
(first-leaf (quotient (+ start end) 2)) ; Can fixnum overflow.
(final-k (let lp ((k i))
(if (>= k first-leaf)
k ; Leaf, so done.
(let* ((k*2-start (+ k (- k start))) ; Don't overflow.
(child1 (+ 1 k*2-start))
(child2 (+ 2 k*2-start))
(child1-val (vector-ref v child1)))
(call-with-values
(lambda ()
(if (< child2 end)
(let ((child2-val (vector-ref v child2)))
(if (elt< child2-val child1-val)
(values child1 child1-val)
(values child2 child2-val)))
(values child1 child1-val)))
(lambda (max-child max-child-val)
(cond ((elt< vi max-child-val)
(vector-set! v k max-child-val)
(lp max-child))
(else k))))))))) ; Done.
(vector-set! v final-k vi)))
;; Put the unsorted subvector V[start,end) into heap order.
(let ((first-leaf (quotient (+ start end) 2))) ; Can fixnum overflow.
(do ((i (- first-leaf 1) (- i 1)))
((< i start))
(restore-heap! end i)))
(do ((i (- end 1) (- i 1)))
((<= i start))
(let ((top (vector-ref v start)))
(vector-set! v start (vector-ref v i))
(vector-set! v i top)
(restore-heap! i start))))
;;; Here are the two exported interfaces.
(define (vector-heap-sort! elt< v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(really-vector-heap-sort! elt< v start end))))
(define (vector-heap-sort elt< v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(let ((ans (vector-portion-copy v start end)))
(really-vector-heap-sort! elt< ans 0 (- end start))
ans))))
;;; Notes on porting
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Bumming the code for speed
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; If you can use a module system to lock up the internal function
;;; REALLY-VECTOR-HEAP-SORT! so that it can only be called from VECTOR-HEAP-SORT and
;;; VECTOR-HEAP-SORT!, then you can hack the internal functions to run with no safety
;;; checks. The safety checks performed by the exported functions VECTOR-HEAP-SORT &
;;; VECTOR-HEAP-SORT! guarantee that there will be no type errors or array-indexing
;;; errors. In addition, with the exception of the two computations of
;;; FIRST-LEAF, all arithmetic will be fixnum arithmetic that never overflows
;;; into bignums, assuming your Scheme provides that you can't allocate an
;;; array so large you might need a bignum to index an element, which is
;;; definitely the case for every implementation with which I am familiar.
;;;
;;; If you want to code up the first-leaf = (quotient (+ s e) 2) computation
;;; so that it will never fixnum overflow when S & E are fixnums, you can do
;;; it this way:
;;; - compute floor(e/2), which throws away e's low-order bit.
;;; - add e's low-order bit to s, and divide that by two:
;;; floor[(s + e mod 2) / 2]
;;; - add these two parts together.
;;; giving you
;;; (+ (quotient e 2)
;;; (quotient (+ s (modulo e 2)) 2))
;;; If we know that e & s are fixnums, and that 0 <= s <= e, then this
;;; can only fixnum-overflow when s = e = max-fixnum. Note that the
;;; two divides and one modulo op can be done very quickly with two
;;; right-shifts and a bitwise and.
;;;
;;; I suspect there has never been a heapsort written in the history of
;;; the world in C that got this detail right.
;;;
;;; If your Scheme has a faster mechanism for handling optional arguments
;;; (e.g., Chez), you should definitely port over to it. Note that argument
;;; defaulting and error-checking are interleaved -- you don't have to
;;; error-check defaulted START/END args to see if they are fixnums that are
;;; legal vector indices for the corresponding vector, etc.
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/visort.scm 0000664 0000000 0000000 00000006276 14737542645 0021344 0 ustar 00root root 0000000 0000000 ;;; The sort package -- stable vector insertion sort -*- Scheme -*-
;;; Copyright (c) 1998 by Olin Shivers.
;;; This code is open-source; see the end of the file for porting and
;;; more copyright information.
;;; Olin Shivers 10/98.
;;; Exports:
;;; vector-insert-sort < v [start end] -> vector
;;; vector-insert-sort! < v [start end] -> unspecific
;;;
;;; %vector-insert-sort! is also called from vqsort.scm's quick-sort function.
(define (vector-insert-sort elt< v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(let ((ans (vector-portion-copy v start end)))
(%vector-insert-sort! elt< ans 0 (- end start))
ans))))
(define (vector-insert-sort! < v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(%vector-insert-sort! < v start end))))
(define (%vector-insert-sort! elt< v start end)
(do ((i (+ 1 start) (+ i 1))) ; Invariant: [start,i) is sorted.
((>= i end))
(let ((val (vector-ref v i)))
(vector-set! v (let lp ((j i)) ; J is the location of the
(if (<= j start)
start ; "hole" as it bubbles down.
(let* ((j-1 (- j 1))
(vj-1 (vector-ref v j-1)))
(cond ((elt< val vj-1)
(vector-set! v j vj-1)
(lp j-1))
(else j)))))
val))))
;;; Copyright
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code is
;;; Copyright (c) 1998 by Olin Shivers.
;;; The terms are: You may do as you please with this code, as long as
;;; you do not delete this notice or hold me responsible for any outcome
;;; related to its use.
;;;
;;; Blah blah blah. Don't you think source files should contain more lines
;;; of code than copyright notice?
;;; Code tuning & porting
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This code is tightly bummed as far as I can go in portable Scheme.
;;;
;;; The code can be converted to use unsafe vector-indexing and
;;; fixnum-specific arithmetic ops -- the safety checks done on entry
;;; to VECTOR-INSERT-SORT and VECTOR-INSERT-SORT! are sufficient to
;;; guarantee nothing bad will happen. However, note that if you alter
;;; %VECTOR-INSERT-SORT! to use dangerous primitives, you must ensure
;;; it is only called from clients that guarantee to observe its
;;; preconditions. In the implementation, %VECTOR-INSERT-SORT! is only
;;; called from VECTOR-INSERT-SORT! and the quick-sort code in
;;; vqsort.scm, and the preconditions are guaranteed for these two
;;; clients. This should provide *big* speedups. In fact, all the
;;; code bumming I've done pretty much disappears in the noise unless
;;; you have a good compiler and also can dump the vector-index checks
;;; and generic arithmetic -- so I've really just set things up for
;;; you to exploit.
;;;
;;; If your Scheme has a faster mechanism for handling optional arguments
;;; (e.g., Chez), you should definitely port over to it. Note that argument
;;; defaulting and error-checking are interleaved -- you don't have to
;;; error-check defaulted START/END args to see if they are fixnums that are
;;; legal vector indices for the corresponding vector, etc.
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/vmsort.scm 0000664 0000000 0000000 00000022366 14737542645 0021346 0 ustar 00root root 0000000 0000000 ;;; The sort package -- stable vector merge & merge sort -*- Scheme -*-
;;; Copyright (c) 1998 by Olin Shivers.
;;; This code is open-source; see the end of the file for porting and
;;; more copyright information.
;;; Olin Shivers 10/98.
;;; Exports:
;;; (vector-merge < v1 v2 [start1 end1 start2 end2]) -> vector
;;; (vector-merge! < v v1 v2 [start0 start1 end1 start2 end2]) -> unspecific
;;;
;;; (vector-merge-sort < v [start end temp]) -> vector
;;; (vector-merge-sort! < v [start end temp]) -> unspecific
;;; Merge
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; (vector-merge < v1 v2 [start1 end1 start2 end2]) -> vector
;;; (vector-merge! < v v1 v2 [start start1 end1 start2 end2]) -> unspecific
;;;
;;; Stable vector merge -- V1's elements come out ahead of equal V2 elements.
(define (vector-merge < v1 v2 . maybe-starts+ends)
(call-with-values
(lambda () (vectors-start+end-2 v1 v2 maybe-starts+ends))
(lambda (start1 end1 start2 end2)
(let ((ans (make-vector (+ (- end1 start1) (- end2 start2)))))
(%vector-merge! < ans v1 v2 0 start1 end1 start2 end2)
ans))))
(define (vector-merge! < v v1 v2 . maybe-starts+ends)
(call-with-values
(lambda ()
(if (pair? maybe-starts+ends)
(values (car maybe-starts+ends)
(cdr maybe-starts+ends))
(values 0
'())))
(lambda (start rest)
(call-with-values
(lambda () (vectors-start+end-2 v1 v2 rest))
(lambda (start1 end1 start2 end2)
(%vector-merge! < v v1 v2 start start1 end1 start2 end2))))))
;;; This routine is not exported. The code is tightly bummed.
;;;
;;; If these preconditions hold, the routine can be bummed to run with
;;; unsafe vector-indexing and fixnum arithmetic ops:
;;; - V V1 V2 are vectors.
;;; - START START1 END1 START2 END2 are fixnums.
;;; - (<= 0 START END0 (vector-length V),
;;; where end0 = start + (end1 - start1) + (end2 - start2)
;;; - (<= 0 START1 END1 (vector-length V1))
;;; - (<= 0 START2 END2 (vector-length V2))
;;; If you put these error checks in the two client procedures above, you can
;;; safely convert this procedure to use unsafe ops -- which is why it isn't
;;; exported. This will provide *huge* speedup.
(define (%vector-merge! elt< v v1 v2 start start1 end1 start2 end2)
(letrec ((vblit (lambda (fromv j i end) ; Blit FROMV[J,END) to V[I,?].
(let lp ((j j) (i i))
(vector-set! v i (vector-ref fromv j))
(let ((j (+ j 1)))
(if (< j end) (lp j (+ i 1))))))))
(cond ((<= end1 start1) (if (< start2 end2) (vblit v2 start2 start end2)))
((<= end2 start2) (vblit v1 start1 start end1))
;; Invariants: I is next index of V to write; X = V1[J]; Y = V2[K].
(else (let lp ((i start)
(j start1) (x (vector-ref v1 start1))
(k start2) (y (vector-ref v2 start2)))
(let ((i1 (+ i 1))) ; "i+1" is a complex number in R4RS!
(if (elt< y x)
(let ((k (+ k 1)))
(vector-set! v i y)
(if (< k end2)
(lp i1 j x k (vector-ref v2 k))
(vblit v1 j i1 end1)))
(let ((j (+ j 1)))
(vector-set! v i x)
(if (< j end1)
(lp i1 j (vector-ref v1 j) k y)
(vblit v2 k i1 end2))))))))))
;;; (vector-merge-sort < v [start end temp]) -> vector
;;; (vector-merge-sort! < v [start end temp]) -> unspecific
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Stable natural vector merge sort
(define (vector-merge-sort! < v . maybe-args)
(call-with-values
(lambda () (vector-start+end v maybe-args))
(lambda (start end)
(let ((temp (if (and (pair? maybe-args) ; kludge
(pair? (cdr maybe-args))
(pair? (cddr maybe-args)))
(caddr maybe-args)
(vector-copy v))))
(%vector-merge-sort! < v start end temp)))))
(define (vector-merge-sort < v . maybe-args)
(call-with-values
(lambda () (vector-start+end v maybe-args))
(lambda (start end)
(let ((ans (r7rs-vector-copy v start end)))
(vector-merge-sort! < ans)
ans))))
;;; %VECTOR-MERGE-SORT! is not exported.
;;; Preconditions:
;;; V TEMP vectors
;;; START END fixnums
;;; START END legal indices for V and TEMP
;;; If these preconditions are ensured by the cover functions, you
;;; can safely change this code to use unsafe fixnum arithmetic and vector
;;; indexing ops, for *huge* speedup.
;;; This merge sort is "opportunistic" -- the leaves of the merge tree are
;;; contiguous runs of already sorted elements in the vector. In the best
;;; case -- an already sorted vector -- it runs in linear time. Worst case
;;; is still O(n lg n) time.
(define (%vector-merge-sort! elt< v0 l r temp0)
(define (xor a b) (not (eq? a b)))
;; Merge v1[l,l+len1) and v2[l+len1,l+len1+len2) into target[l,l+len1+len2)
;; Merge left-to-right, so that TEMP may be either V1 or V2
;; (that this is OK takes a little bit of thought).
;; V2=TARGET? is true if V2 and TARGET are the same, which allows
;; merge to punt the final blit half of the time.
(define (merge target v1 v2 l len1 len2 v2=target?)
(letrec ((vblit (lambda (fromv j i end) ; Blit FROMV[J,END) to TARGET[I,?]
(let lp ((j j) (i i)) ; J < END. The final copy.
(vector-set! target i (vector-ref fromv j))
(let ((j (+ j 1)))
(if (< j end) (lp j (+ i 1))))))))
(let* ((r1 (+ l len1))
(r2 (+ r1 len2)))
; Invariants:
(let lp ((n l) ; N is next index of
(j l) (x (vector-ref v1 l)) ; TARGET to write.
(k r1) (y (vector-ref v2 r1))) ; X = V1[J]
(let ((n+1 (+ n 1))) ; Y = V2[K]
(if (elt< y x)
(let ((k (+ k 1)))
(vector-set! target n y)
(if (< k r2)
(lp n+1 j x k (vector-ref v2 k))
(vblit v1 j n+1 r1)))
(let ((j (+ j 1)))
(vector-set! target n x)
(if (< j r1)
(lp n+1 j (vector-ref v1 j) k y)
(if (not v2=target?) (vblit v2 k n+1 r2))))))))))
;; Might hack GETRUN so that if the run is short it pads it out to length
;; 10 with insert sort...
;; Precondition: l < r.
(define (getrun v l r)
(let lp ((i (+ l 1)) (x (vector-ref v l)))
(if (>= i r)
(- i l)
(let ((y (vector-ref v i)))
(if (elt< y x)
(- i l)
(lp (+ i 1) y))))))
;; RECUR: Sort V0[L,L+LEN) for some LEN where 0 < WANT <= LEN <= (R-L).
;; That is, sort *at least* WANT elements in V0 starting at index L.
;; May put the result into either V0[L,L+LEN) or TEMP0[L,L+LEN).
;; Must not alter either vector outside this range.
;; Return:
;; - LEN -- the number of values we sorted
;; - ANSVEC -- the vector holding the value
;; - ANS=V0? -- tells if ANSVEC is V0 or TEMP
;;
;; LP: V[L,L+PFXLEN) holds a sorted prefix of V0.
;; TEMP = if V = V0 then TEMP0 else V0. (I.e., TEMP is the other vec.)
;; PFXLEN2 is a power of 2 <= PFXLEN.
;; Solve RECUR's problem.
(if (< l r) ; Don't try to sort an empty range.
(call-with-values
(lambda ()
(let recur ((l l) (want (- r l)))
(let ((len (- r l)))
(let lp ((pfxlen (getrun v0 l r)) (pfxlen2 1)
(v v0) (temp temp0)
(v=v0? #t))
(if (or (>= pfxlen want) (= pfxlen len))
(values pfxlen v v=v0?)
(let ((pfxlen2 (let lp ((j pfxlen2))
(let ((j*2 (+ j j)))
(if (<= j pfxlen) (lp j*2) j))))
(tail-len (- len pfxlen)))
;; PFXLEN2 is now the largest power of 2 <= PFXLEN.
;; (Just think of it as being roughly PFXLEN.)
(call-with-values
(lambda ()
(recur (+ pfxlen l) pfxlen2))
(lambda (nr-len nr-vec nrvec=v0?)
(merge temp v nr-vec l pfxlen nr-len
(xor nrvec=v0? v=v0?))
(lp (+ pfxlen nr-len) (+ pfxlen2 pfxlen2)
temp v (not v=v0?))))))))))
(lambda (ignored-len ignored-ansvec ansvec=v0?)
(if (not ansvec=v0?)
(r7rs-vector-copy! v0 l temp0 l r))))))
;;; Copyright
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code is
;;; Copyright (c) 1998 by Olin Shivers.
;;; The terms are: You may do as you please with this code, as long as
;;; you do not delete this notice or hold me responsible for any outcome
;;; related to its use.
;;;
;;; Blah blah blah. Don't you think source files should contain more lines
;;; of code than copyright notice?
;;; Code tuning & porting
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code is *tightly* bummed as far as I can go in portable Scheme.
;;;
;;; The two internal primitives that do the real work can be converted to
;;; use unsafe vector-indexing and fixnum-specific arithmetic ops *if* you
;;; alter the four small cover functions to enforce the invariants. This should
;;; provide *big* speedups. In fact, all the code bumming I've done pretty
;;; much disappears in the noise unless you have a good compiler and also
;;; can dump the vector-index checks and generic arithmetic -- so I've really
;;; just set things up for you to exploit.
;;;
;;; The optional-arg parsing, defaulting, and error checking is done with a
;;; portable R4RS macro. But if your Scheme has a faster mechanism (e.g.,
;;; Chez), you should definitely port over to it. Note that argument defaulting
;;; and error-checking are interleaved -- you don't have to error-check
;;; defaulted START/END args to see if they are fixnums that are legal vector
;;; indices for the corresponding vector, etc.
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/vqsort2.scm 0000664 0000000 0000000 00000017556 14737542645 0021441 0 ustar 00root root 0000000 0000000 ;;; The SRFI-32 sort package -- quick sort -*- Scheme -*-
;;; Copyright (c) 2002 by Olin Shivers.
;;; This code is open-source; see the end of the file for porting and
;;; more copyright information.
;;; Olin Shivers 2002/7.
;;; (quick-sort < v [start end]) -> vector
;;; (quick-sort! < v [start end]) -> unspecific
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The algorithm is a standard quicksort, but the partition loop is fancier,
;;; arranging the vector into a left part that is <, a middle region that is
;;; =, and a right part that is > the pivot. Here's how it is done:
;;; The partition loop divides the range being partitioned into five
;;; subranges:
;;; =======<<<<<<<<????????>>>>>>>=======
;;; where = marks a value that is equal the pivot, < marks a value that
;;; is less than the pivot, ? marks a value that hasn't been scanned, and
;;; > marks a value that is greater than the pivot. Let's consider the
;;; left-to-right scan. If it checks a ? value that is <, it keeps scanning.
;;; If the ? value is >, we stop the scan -- we are ready to start the
;;; right-to-left scan and then do a swap. But if the rightward scan checks
;;; a ? value that is =, we swap it *down* to the end of the initial chunk
;;; of ====='s -- we exchange it with the leftmost < value -- and then
;;; continue our rightward scan. The leftwards scan works in a similar
;;; fashion, scanning past > elements, stopping on a < element, and swapping
;;; up = elements. When we are done, we have a picture like this
;;; ========<<<<<<<<<<<<>>>>>>>>>>=========
;;; Then swap the = elements up into the middle of the vector to get
;;; this:
;;; <<<<<<<<<<<<=================>>>>>>>>>>
;;; Then recurse on the <'s and >'s. Work out all the tricky little
;;; boundary cases, and you're done.
;;;
;;; Other tricks:
;;; - This quicksort also makes some effort to pick the pivot well -- it uses
;;; the median of three elements as the partition pivot, so pathological n^2
;;; run time is much rarer (but not eliminated completely). If you really
;;; wanted to get fancy, you could use a random number generator to choose
;;; pivots. The key to this trick is that you only need to pick one random
;;; number for each *level* of recursion -- i.e. you only need (lg n) random
;;; numbers.
;;; - After the partition, we *recurse* on the smaller of the two pending
;;; regions, then *tail-recurse* (iterate) on the larger one. This guarantees
;;; we use no more than lg(n) stack frames, worst case.
;;; - There are two ways to finish off the sort.
;;; A Recurse down to regions of size 10, then sort each such region using
;;; insertion sort.
;;; B Recurse down to regions of size 10, then sort *the entire vector*
;;; using insertion sort.
;;; We do A. Each choice has a cost. Choice A has more overhead to invoke
;;; all the separate insertion sorts -- choice B only calls insertion sort
;;; once. But choice B will call the comparison function *more times* --
;;; it will unnecessarily compare elt 9 of one segment to elt 0 of the
;;; following segment. The overhead of choice A is linear in the length
;;; of the vector, but *otherwise independent of the algorithm's parameters*.
;;; I.e., it's a *fixed*, *small* constant factor. The cost of the extra
;;; comparisons made by choice B, however, is dependent on an externality:
;;; the comparison function passed in by the client. This can be made
;;; arbitrarily bad -- that is, the constant factor *isn't* fixed by the
;;; sort algorithm; instead, it's determined by the comparison function.
;;; If your comparison function is very, very slow, you want to eliminate
;;; every single one that you can. Choice A limits the potential badness,
;;; so that is what we do.
(define (vector-quick-sort! < v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(%quick-sort! < v start end))))
(define (vector-quick-sort < v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(let ((ans (make-vector (- end start))))
(vector-portion-copy! ans v start end)
(%quick-sort! < ans 0 (- end start))
ans))))
;;; %QUICK-SORT is not exported.
;;; Preconditions:
;;; V vector
;;; START END fixnums
;;; 0 <= START, END <= (vector-length V)
;;; If these preconditions are ensured by the cover functions, you
;;; can safely change this code to use unsafe fixnum arithmetic and vector
;;; indexing ops, for *huge* speedup.
;;;
;;; We bail out to insertion sort for small ranges; feel free to tune the
;;; crossover -- it's just a random guess. If you don't have the insertion
;;; sort routine, just kill that branch of the IF and change the recursion
;;; test to (< 1 (- r l)) -- the code is set up to work that way.
(define (%quick-sort! elt< v start end)
;; Swap the N outer pairs of the range [l,r).
(define (swap l r n)
(if (> n 0)
(let ((x (vector-ref v l))
(r-1 (- r 1)))
(vector-set! v l (vector-ref v r-1))
(vector-set! v r-1 x)
(swap (+ l 1) r-1 (- n 1)))))
;; Choose the median of V[l], V[r], and V[middle] for the pivot.
(define (median v1 v2 v3)
(call-with-values
(lambda () (if (elt< v1 v2) (values v1 v2) (values v2 v1)))
(lambda (little big)
(if (elt< big v3)
big
(if (elt< little v3) v3 little)))))
(let recur ((l start) (r end)) ; Sort the range [l,r).
(if (< 10 (- r l)) ; Ten: the gospel according to Sedgewick.
(let ((pivot (median (vector-ref v l)
(vector-ref v (quotient (+ l r) 2))
(vector-ref v (- r 1)))))
;; Everything in these loops is driven by the invariants expressed
;; in the little pictures & the corresponding l,i,j,k,m,r indices
;; and the associated ranges.
;; =======<<<<<<<<????????>>>>>>>=======
;; l i j k m r
;; [l,i) [i,j) [j,k] (k,m] (m,r)
(letrec ((lscan (lambda (i j k m) ; left-to-right scan
(let lp ((i i) (j j))
(if (> j k)
(done i j m)
(let ((x (vector-ref v j)))
(cond ((elt< x pivot) (lp i (+ j 1)))
((elt< pivot x) (rscan i j k m))
(else ; Equal
(if (< i j)
(begin (vector-set! v j (vector-ref v i))
(vector-set! v i x)))
(lp (+ i 1) (+ j 1)))))))))
;; =======<<<<<<<<<>????????>>>>>>>=======
;; l i j k m r
;; [l,i) [i,j) j (j,k] (k,m] (m,r)
(rscan (lambda (i j k m) ; right-to-left scan
(let lp ((k k) (m m))
(if (<= k j)
(done i j m)
(let* ((x (vector-ref v k)))
(cond ((elt< pivot x) (lp (- k 1) m))
((elt< x pivot) ; Swap j & k & lscan.
(vector-set! v k (vector-ref v j))
(vector-set! v j x)
(lscan i (+ j 1) (- k 1) m))
(else ; x=pivot
(if (< k m)
(begin (vector-set! v k (vector-ref v m))
(vector-set! v m x)))
(lp (- k 1) (- m 1)))))))))
;; =======<<<<<<<<<<<<<>>>>>>>>>>>=======
;; l i j m r
;; [l,i) [i,j) [j,m] (m,r)
(done (lambda (i j m)
(let ((num< (- j i))
(num> (+ 1 (- m j)))
(num=l (- i l))
(num=r (- (- r m) 1)))
(swap l j (min num< num=l)) ; Swap ='s into
(swap j r (min num> num=r)) ; the middle.
;; Recur on the <'s and >'s. Recurring on the
;; smaller range and iterating on the bigger
;; range ensures O(lg n) stack frames, worst case.
(cond ((<= num< num>)
(recur l (+ l num<))
(recur (- r num>) r))
(else
(recur (- r num>) r)
(recur l (+ l num<))))))))
(let ((r-1 (- r 1)))
(lscan l l r-1 r-1))))
;; Small segment => punt to insert sort.
;; Use the dangerous subprimitive.
(%vector-insert-sort! elt< v l r))))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a132/vqsort3.scm 0000664 0000000 0000000 00000026323 14737542645 0021432 0 ustar 00root root 0000000 0000000 ;;; The SRFI-32 sort package -- three-way quick sort -*- Scheme -*-
;;; Copyright (c) 2002 by Olin Shivers.
;;; This code is open-source; see the end of the file for porting and
;;; more copyright information.
;;; Olin Shivers 2002/7.
;;; (quick-sort3! c v [start end]) -> unspecific
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Sort vector V[start,end) using three-way comparison function C:
;;; (c x y) < 0 => x x=y
;;; (c x y) > 0 => x>y
;;; That is, C acts as a sort of "subtraction" procedure; using - for the
;;; comparison function will cause numbers to be sorted into increasing order.
;;;
;;; This algorithm is more efficient than standard, two-way quicksort if there
;;; are many duplicate items in the data set and the comparison function is
;;; relatively expensive (e.g., comparing large strings). It is due to Jon
;;; Bentley & Doug McIlroy; I learned it from Bentley.
;;;
;;; The algorithm is a standard quicksort, but the partition loop is fancier,
;;; arranging the vector into a left part that is <, a middle region that is
;;; =, and a right part that is > the pivot. Here's how it is done:
;;; The partition loop divides the range being partitioned into five
;;; subranges:
;;; =======<<<<<<<<????????>>>>>>>=======
;;; where = marks a value that is equal the pivot, < marks a value that
;;; is less than the pivot, ? marks a value that hasn't been scanned, and
;;; > marks a value that is greater than the pivot. Let's consider the
;;; left-to-right scan. If it checks a ? value that is <, it keeps scanning.
;;; If the ? value is >, we stop the scan -- we are ready to start the
;;; right-to-left scan and then do a swap. But if the rightward scan checks
;;; a ? value that is =, we swap it *down* to the end of the initial chunk
;;; of ====='s -- we exchange it with the leftmost < value -- and then
;;; continue our rightward scan. The leftwards scan works in a similar
;;; fashion, scanning past > elements, stopping on a < element, and swapping
;;; up = elements. When we are done, we have a picture like this
;;; ========<<<<<<<<<<<<>>>>>>>>>>=========
;;; Then swap the = elements up into the middle of the vector to get
;;; this:
;;; <<<<<<<<<<<<=================>>>>>>>>>>
;;; Then recurse on the <'s and >'s. Work out all the tricky little
;;; boundary cases, and you're done.
;;;
;;; Other tricks that make this implementation industrial strength:
;;; - This quicksort makes some effort to pick the pivot well -- it uses the
;;; median of three elements as the partition pivot, so pathological n^2
;;; run time is much rarer (but not eliminated completely). If you really
;;; wanted to get fancy, you could use a random number generator to choose
;;; pivots. The key to this trick is that you only need to pick one random
;;; number for each *level* of recursion -- i.e. you only need (lg n) random
;;; numbers.
;;;
;;; - After the partition, we *recurse* on the smaller of the two pending
;;; regions, then *tail-recurse* (iterate) on the larger one. This guarantees
;;; we use no more than lg(n) stack frames, worst case.
;;;
;;; - There are two ways to finish off the sort.
;;; A. Recurse down to regions of size 10, then sort each such region using
;;; insertion sort.
;;; B. Recurse down to regions of size 10, then sort *the entire vector*
;;; using insertion sort.
;;; We do A. Each choice has a cost. Choice A has more overhead to invoke
;;; all the separate insertion sorts -- choice B only calls insertion sort
;;; once. But choice B will call the comparison function *more times* --
;;; it will unnecessarily compare elt 9 of one segment to elt 0 of the
;;; following segment. The overhead of choice A is linear in the length
;;; of the vector, but *otherwise independent of the algorithm's parameters*.
;;; I.e., it's a *fixed*, *small* constant factor. The cost of the extra
;;; comparisons made by choice B, however, is dependent on an externality:
;;; the comparison function passed in by the client. This can be made
;;; arbitrarily bad -- that is, the constant factor *isn't* fixed by the
;;; sort algorithm; instead, it's determined by the comparison function.
;;; If your comparison function is very, very slow, you want to eliminate
;;; every single one that you can. Choice A limits the potential badness,
;;; so that is what we do.
(define (vector-quick-sort3! c v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(%quick-sort3! c v start end))))
(define (vector-quick-sort3 c v . maybe-start+end)
(call-with-values
(lambda () (vector-start+end v maybe-start+end))
(lambda (start end)
(let ((ans (make-vector (- end start))))
(vector-portion-copy! ans v start end)
(%quick-sort3! c ans 0 (- end start))
ans))))
;;; %QUICK-SORT3! is not exported.
;;; Preconditions:
;;; V vector
;;; START END fixnums
;;; 0 <= START, END <= (vector-length V)
;;; If these preconditions are ensured by the cover functions, you
;;; can safely change this code to use unsafe fixnum arithmetic and vector
;;; indexing ops, for *huge* speedup.
;;;
;;; We bail out to insertion sort for small ranges; feel free to tune the
;;; crossover -- it's just a random guess. If you don't have the insertion
;;; sort routine, just kill that branch of the IF and change the recursion
;;; test to (< 1 (- r l)) -- the code is set up to work that way.
(define (%quick-sort3! c v start end)
(define (swap l r n) ; Little utility -- swap the N
(if (> n 0)
(let ((x (vector-ref v l)) ; outer pairs of the range [l,r).
(r-1 (- r 1)))
(vector-set! v l (vector-ref v r-1))
(vector-set! v r-1 x)
(swap (+ l 1) r-1 (- n 1)))))
(define (sort3 v1 v2 v3)
(call-with-values
(lambda () (if (< (c v1 v2) 0) (values v1 v2) (values v2 v1)))
(lambda (little big)
(if (< (c big v3) 0)
(values little big v3)
(if (< (c little v3) 0)
(values little v3 big)
(values v3 little big))))))
(define (elt< v1 v2)
(negative? (c v1 v2)))
(let recur ((l start) (r end)) ; Sort the range [l,r).
(if (< 10 (- r l)) ; 10: the gospel according to Sedgewick.
;; Choose the median of V[l], V[r-1], and V[middle] for the pivot.
;; We do this by sorting these three elts; call the results LO, PIVOT
;; & HI. Put LO, PIVOT & HI where they should go in the vector. We
;; will kick off the partition step with one elt (PIVOT) in the left=
;; range, one elt (LO) in the < range, one elt (HI) in in the > range
;; & no elts in the right= range.
(let* ((r-1 (- r 1)) ; Three handy
(mid (quotient (+ l r) 2)) ; common
(l+1 (+ l 1)) ; subexpressions
(pivot (call-with-values
(lambda ()
(sort3 (vector-ref v l)
(vector-ref v mid)
(vector-ref v r-1)))
(lambda (lo piv hi)
(let ((tmp (vector-ref v l+1))) ; Put LO, PIV & HI
(vector-set! v l piv) ; back into V
(vector-set! v r-1 hi) ; where they belong,
(vector-set! v l+1 lo)
(vector-set! v mid tmp)
piv))))) ; and return PIV as pivot.
;; Everything in these loops is driven by the invariants expressed
;; in the little pictures, the corresponding l,i,j,k,m,r indices,
;; & the associated ranges.
;; =======<<<<<<<<????????>>>>>>>======= (picture)
;; l i j k m r (indices)
;; [l,i) [i,j) [j,k] (k,m] (m,r) (ranges )
(letrec ((lscan (lambda (i j k m) ; left-to-right scan
(let lp ((i i) (j j))
(if (> j k)
(done i j m)
(let* ((x (vector-ref v j))
(sign (c x pivot)))
(cond ((< sign 0) (lp i (+ j 1)))
((= sign 0)
(if (< i j)
(begin (vector-set! v j (vector-ref v i))
(vector-set! v i x)))
(lp (+ i 1) (+ j 1)))
((> sign 0) (rscan i j k m))))))))
;; =======<<<<<<<<<>????????>>>>>>>=======
;; l i j k m r
;; [l,i) [i,j) j (j,k] (k,m] (m,r)
(rscan (lambda (i j k m) ; right-to-left scan
(let lp ((k k) (m m))
(if (<= k j)
(done i j m)
(let* ((x (vector-ref v k))
(sign (c x pivot)))
(cond ((> sign 0) (lp (- k 1) m))
((= sign 0)
(if (< k m)
(begin (vector-set! v k (vector-ref v m))
(vector-set! v m x)))
(lp (- k 1) (- m 1)))
((< sign 0) ; Swap j & k & lscan.
(vector-set! v k (vector-ref v j))
(vector-set! v j x)
(lscan i (+ j 1) (- k 1) m))))))))
;; =======<<<<<<<<<<<<<>>>>>>>>>>>=======
;; l i j m r
;; [l,i) [i,j) [j,m] (m,r)
(done (lambda (i j m)
(let ((num< (- j i))
(num> (+ 1 (- m j)))
(num=l (- i l))
(num=r (- (- r m) 1)))
(swap l j (min num< num=l)) ; Swap ='s into
(swap j r (min num> num=r)) ; the middle.
;; Recur on the <'s and >'s. Recurring on the
;; smaller range and iterating on the bigger
;; range ensures O(lg n) stack frames, worst case.
(cond ((<= num< num>)
(recur l (+ l num<))
(recur (- r num>) r))
(else
(recur (- r num>) r)
(recur l (+ l num<))))))))
;; To repeat: We kick off the partition step with one elt (PIVOT)
;; in the left= range, one elt (LO) in the < range, one elt (HI)
;; in the > range & no elts in the right= range.
(lscan l+1 (+ l 2) (- r 2) r-1)))
;; Small segment => punt to insert sort.
;; Use the dangerous subprimitive.
(%vector-insert-sort! elt< v l r))))
;;; Copyright
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code is
;;; Copyright (c) 1998 by Olin Shivers.
;;; The terms are: You may do as you please with this code, as long as
;;; you do not delete this notice or hold me responsible for any outcome
;;; related to its use.
;;;
;;; Blah blah blah.
;;; Code tuning & porting
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; - The quicksort recursion bottoms out in a call to an insertion sort
;;; routine, %INSERT-SORT!. But you could even punt this and go with pure
;;; recursion in a pinch.
;;;
;;; This code is *tightly* bummed as far as I can go in portable Scheme.
;;;
;;; The internal primitive %QUICK-SORT! that does the real work can be
;;; converted to use unsafe vector-indexing and fixnum-specific arithmetic ops
;;; *if* you alter the two small cover functions to enforce the invariants.
;;; This should provide *big* speedups. In fact, all the code bumming I've
;;; done pretty much disappears in the noise unless you have a good compiler
;;; and also can dump the vector-index checks and generic arithmetic -- so
;;; I've really just set things up for you to exploit.
;;;
;;; The optional-arg parsing, defaulting, and error checking is done with a
;;; portable R4RS macro. But if your Scheme has a faster mechanism (e.g.,
;;; Chez), you should definitely port over to it. Note that argument defaulting
;;; and error-checking are interleaved -- you don't have to error-check
;;; defaulted START/END args to see if they are fixnums that are legal vector
;;; indices for the corresponding vector, etc.
chez-srfi-0.0+git20241031.b424440+dfsg/%3a133.sls 0000664 0000000 0000000 00000001226 14737542645 0020024 0 ustar 00root root 0000000 0000000 (library (srfi :133)
(export
;; Constructors
vector-unfold vector-unfold-right vector-reverse-copy
vector-concatenate vector-append-subvectors
;; Predicates
vector-empty? vector=
;; Iteration
vector-fold vector-fold-right vector-map!
vector-count vector-cumulate
;; Searching
vector-index vector-index-right vector-skip vector-skip-right
vector-binary-search vector-any vector-every vector-partition
;; Mutators
vector-swap! vector-reverse!
vector-reverse-copy! vector-unfold! vector-unfold-right!
;; Conversion
reverse-vector->list reverse-list->vector)
(import (srfi :133 vectors)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a133/ 0000775 0000000 0000000 00000000000 14737542645 0017300 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a133/vectors-impl.scm 0000775 0000000 0000000 00000166641 14737542645 0022451 0 ustar 00root root 0000000 0000000 ;;;;;; SRFI 43: Vector library -*- Scheme -*-
;;;
;;; $Id$
;;;
;;; Taylor Campbell wrote this code; he places it in the public domain.
;;; Will Clinger [wdc] made some corrections, also in the public domain.
;;; John Cowan modified this code for SRFI 133; his changes are also in
;;; the public domain. However, in jurisdictions where it is not possible
;;; to dedicate something to the public domain, the entire implementation
;;; is made available under the same license as SRFI 133.
;;; --------------------
;;; Exported procedure index
;;;
;;; * Constructors
;;; vector-unfold vector-unfold-right
;;; vector-copy vector-reverse-copy
;;; vector-append vector-concatenate
;;; vector-append-subvectors
;;;
;;; * Predicates
;;; vector-empty?
;;; vector=
;;;
;;; * Iteration
;;; vector-fold vector-fold-right
;;; vector-map vector-map!
;;; vector-for-each
;;; vector-count vector-cumulate
;;;
;;; * Searching
;;; vector-index vector-skip
;;; vector-index-right vector-skip-right
;;; vector-binary-search
;;; vector-any vector-every
;;; vector-partition
;;;
;;; * Mutators
;;; vector-swap!
;;; vector-fill!
;;; vector-reverse!
;;; vector-copy! vector-reverse-copy!
;;; vector-reverse!
;;; vector-unfold! vector-unfold-right!
;;;
;;; * Conversion
;;; vector->list reverse-vector->list
;;; list->vector reverse-list->vector
;;; vector->string string->vector
;;; --------------------
;;; Commentary on efficiency of the code
;;; This code is somewhat tuned for efficiency. There are several
;;; internal routines that can be optimized greatly to greatly improve
;;; the performance of much of the library. These internal procedures
;;; are already carefully tuned for performance, and lambda-lifted by
;;; hand. Some other routines are lambda-lifted by hand, but only the
;;; loops are lambda-lifted, and only if some routine has two possible
;;; loops -- a fast path and an n-ary case --, whereas _all_ of the
;;; internal routines' loops are lambda-lifted so as to never cons a
;;; closure in their body (VECTOR-PARSE-START+END doesn't have a loop),
;;; even in Scheme systems that perform no loop optimization (which is
;;; most of them, unfortunately).
;;;
;;; Fast paths are provided for common cases in most of the loops in
;;; this library.
;;;
;;; All calls to primitive vector operations are protected by a prior
;;; type check; they can be safely converted to use unsafe equivalents
;;; of the operations, if available. Ideally, the compiler should be
;;; able to determine this, but the state of Scheme compilers today is
;;; not a happy one.
;;;
;;; Efficiency of the actual algorithms is a rather mundane point to
;;; mention; vector operations are rarely beyond being straightforward.
;;; --------------------
;;; Utilities
;;; SRFI 8, too trivial to put in the dependencies list.
(define-syntax receive
(syntax-rules ()
((receive ?formals ?producer ?body1 ?body2 ...)
(call-with-values (lambda () ?producer)
(lambda ?formals ?body1 ?body2 ...)))))
;;; Not the best LET*-OPTIONALS, but not the worst, either. Use Olin's
;;; if it's available to you.
(define-syntax let*-optionals
(syntax-rules ()
((let*-optionals (?x ...) ((?var ?default) ...) ?body1 ?body2 ...)
(let ((args (?x ...)))
(let*-optionals args ((?var ?default) ...) ?body1 ?body2 ...)))
((let*-optionals ?args ((?var ?default) ...) ?body1 ?body2 ...)
(let*-optionals:aux ?args ?args ((?var ?default) ...)
?body1 ?body2 ...))))
(define-syntax let*-optionals:aux
(syntax-rules ()
((aux ?orig-args-var ?args-var () ?body1 ?body2 ...)
(if (null? ?args-var)
(let () ?body1 ?body2 ...)
(error "too many arguments" (length ?orig-args-var)
?orig-args-var)))
((aux ?orig-args-var ?args-var
((?var ?default) ?more ...)
?body1 ?body2 ...)
(if (null? ?args-var)
(let* ((?var ?default) ?more ...) ?body1 ?body2 ...)
(let ((?var (car ?args-var))
(new-args (cdr ?args-var)))
(let*-optionals:aux ?orig-args-var new-args
(?more ...)
?body1 ?body2 ...))))))
(define (nonneg-int? x)
(and (integer? x)
(not (negative? x))))
(define (between? x y z)
(and (< x y)
(<= y z)))
(define (unspecified-value) (if #f #f))
;++ This should be implemented more efficiently. It shouldn't cons a
;++ closure, and the cons cells used in the loops when using this could
;++ be reused.
(define (vectors-ref vectors i)
(map (lambda (v) (vector-ref v i)) vectors))
;;; --------------------
;;; Error checking
;;; Error signalling (not checking) is done in a way that tries to be
;;; as helpful to the person who gets the debugging prompt as possible.
;;; That said, error _checking_ tries to be as unredundant as possible.
;;; I don't use any sort of general condition mechanism; I use simply
;;; SRFI 23's ERROR, even in cases where it might be better to use such
;;; a general condition mechanism. Fix that when porting this to a
;;; Scheme implementation that has its own condition system.
;;; In argument checks, upon receiving an invalid argument, the checker
;;; procedure recursively calls itself, but in one of the arguments to
;;; itself is a call to ERROR; this mechanism is used in the hopes that
;;; the user may be thrown into a debugger prompt, proceed with another
;;; value, and let it be checked again.
;;; Type checking is pretty basic, but easily factored out and replaced
;;; with whatever your implementation's preferred type checking method
;;; is. I doubt there will be many other methods of index checking,
;;; though the index checkers might be better implemented natively.
;;; (CHECK-TYPE ) -> value
;;; Ensure that VALUE satisfies TYPE-PREDICATE?; if not, signal an
;;; error stating that VALUE did not satisfy TYPE-PREDICATE?, showing
;;; that this happened while calling CALLEE. Return VALUE if no
;;; error was signalled.
(define (check-type pred? value callee)
(if (pred? value)
value
;; Recur: when (or if) the user gets a debugger prompt, he can
;; proceed where the call to ERROR was with the correct value.
(check-type pred?
(error "erroneous value"
(list pred? value)
`(while calling ,callee))
callee)))
;;; (CHECK-INDEX ) -> index
;;; Ensure that INDEX is a valid index into VECTOR; if not, signal an
;;; error stating that it is not and that this happened in a call to
;;; CALLEE. Return INDEX when it is valid. (Note that this does NOT
;;; check that VECTOR is indeed a vector.)
(define (check-index vec index callee)
(let ((index (check-type integer? index callee)))
(cond ((< index 0)
(check-index vec
(error "vector index too low"
index
`(into vector ,vec)
`(while calling ,callee))
callee))
((>= index (vector-length vec))
(check-index vec
(error "vector index too high"
index
`(into vector ,vec)
`(while calling ,callee))
callee))
(else index))))
;;; (CHECK-INDICES
;;;
;;;
;;; ) -> [start end]
;;; Ensure that START and END are valid bounds of a range within
;;; VECTOR; if not, signal an error stating that they are not, with
;;; the message being informative about what the argument names were
;;; called -- by using START-NAME & END-NAME --, and that it occurred
;;; while calling CALLEE. Also ensure that VEC is in fact a vector.
;;; Returns no useful value.
(define (check-indices vec start start-name end end-name callee)
(let ((lose (lambda things
(apply error "vector range out of bounds"
(append things
`(vector was ,vec)
`(,start-name was ,start)
`(,end-name was ,end)
`(while calling ,callee)))))
(start (check-type integer? start callee))
(end (check-type integer? end callee)))
(cond ((> start end)
;; I'm not sure how well this will work. The intent is that
;; the programmer tells the debugger to proceed with both a
;; new START & a new END by returning multiple values
;; somewhere.
(receive (new-start new-end)
(lose `(,end-name < ,start-name))
(check-indices vec
new-start start-name
new-end end-name
callee)))
((< start 0)
(check-indices vec
(lose `(,start-name < 0))
start-name
end end-name
callee))
((>= start (vector-length vec))
(check-indices vec
(lose `(,start-name > len)
`(len was ,(vector-length vec)))
start-name
end end-name
callee))
((> end (vector-length vec))
(check-indices vec
start start-name
(lose `(,end-name > len)
`(len was ,(vector-length vec)))
end-name
callee))
(else
(values start end)))))
;;; --------------------
;;; Internal routines
;;; These should all be integrated, native, or otherwise optimized --
;;; they're used a _lot_ --. All of the loops and LETs inside loops
;;; are lambda-lifted by hand, just so as not to cons closures in the
;;; loops. (If your compiler can do better than that if they're not
;;; lambda-lifted, then lambda-drop (?) them.)
;;; (VECTOR-PARSE-START+END
;;;
;;; )
;;; -> [start end]
;;; Return two values, composing a valid range within VECTOR, as
;;; extracted from ARGUMENTS or defaulted from VECTOR -- 0 for START
;;; and the length of VECTOR for END --; START-NAME and END-NAME are
;;; purely for error checking.
(define (vector-parse-start+end vec args start-name end-name callee)
(let ((len (vector-length vec)))
(cond ((null? args)
(values 0 len))
((null? (cdr args))
(check-indices vec
(car args) start-name
len end-name
callee))
((null? (cddr args))
(check-indices vec
(car args) start-name
(cadr args) end-name
callee))
(else
(error "too many arguments"
`(extra args were ,(cddr args))
`(while calling ,callee))))))
(define-syntax let-vector-start+end
(syntax-rules ()
((let-vector-start+end ?callee ?vec ?args (?start ?end)
?body1 ?body2 ...)
(let ((?vec (check-type vector? ?vec ?callee)))
(receive (?start ?end)
(vector-parse-start+end ?vec ?args '?start '?end
?callee)
?body1 ?body2 ...)))))
;;; (%SMALLEST-LENGTH )
;;; -> exact, nonnegative integer
;;; Compute the smallest length of VECTOR-LIST. DEFAULT-LENGTH is
;;; the length that is returned if VECTOR-LIST is empty. Common use
;;; of this is in n-ary vector routines:
;;; (define (f vec . vectors)
;;; (let ((vec (check-type vector? vec f)))
;;; ...(%smallest-length vectors (vector-length vec) f)...))
;;; %SMALLEST-LENGTH takes care of the type checking -- which is what
;;; the CALLEE argument is for --; thus, the design is tuned for
;;; avoiding redundant type checks.
(define %smallest-length
(letrec ((loop (lambda (vector-list length callee)
(if (null? vector-list)
length
(loop (cdr vector-list)
(min (vector-length
(check-type vector?
(car vector-list)
callee))
length)
callee)))))
loop))
;;; (%VECTOR-COPY! )
;;; Copy elements at locations SSTART to SEND from SOURCE to TARGET,
;;; starting at TSTART in TARGET.
;;;
;;; Optimize this! Probably with some combination of:
;;; - Force it to be integrated.
;;; - Let it use unsafe vector element dereferencing routines: bounds
;;; checking already happens outside of it. (Or use a compiler
;;; that figures this out, but Olin Shivers' PhD thesis seems to
;;; have been largely ignored in actual implementations...)
;;; - Implement it natively as a VM primitive: the VM can undoubtedly
;;; perform much faster than it can make Scheme perform, even with
;;; bounds checking.
;;; - Implement it in assembly: you _want_ the fine control that
;;; assembly can give you for this.
;;; I already lambda-lift it by hand, but you should be able to make it
;;; even better than that.
(define %vector-copy!
(letrec ((loop/l->r (lambda (target source send i j)
(cond ((< i send)
(vector-set! target j
(vector-ref source i))
(loop/l->r target source send
(+ i 1) (+ j 1))))))
(loop/r->l (lambda (target source sstart i j)
(cond ((>= i sstart)
(vector-set! target j
(vector-ref source i))
(loop/r->l target source sstart
(- i 1) (- j 1)))))))
(lambda (target tstart source sstart send)
(if (> sstart tstart) ; Make sure we don't copy over
; ourselves.
(loop/l->r target source send sstart tstart)
(loop/r->l target source sstart (- send 1)
(+ -1 tstart send (- sstart)))))))
;;; (%VECTOR-REVERSE-COPY! )
;;; Copy elements from SSTART to SEND from SOURCE to TARGET, in the
;;; reverse order.
(define %vector-reverse-copy!
(letrec ((loop (lambda (target source sstart i j)
(cond ((>= i sstart)
(vector-set! target j (vector-ref source i))
(loop target source sstart
(- i 1)
(+ j 1)))))))
(lambda (target tstart source sstart send)
(loop target source sstart
(- send 1)
tstart))))
;;; (%VECTOR-REVERSE! )
(define %vector-reverse!
(letrec ((loop (lambda (vec i j)
(cond ((<= i j)
(let ((v (vector-ref vec i)))
(vector-set! vec i (vector-ref vec j))
(vector-set! vec j v)
(loop vec (+ i 1) (- j 1))))))))
(lambda (vec start end)
(loop vec start (- end 1)))))
;;; (%VECTOR-FOLD1 ) -> knil'
;;; (KONS ) -> knil'
(define %vector-fold1
(letrec ((loop (lambda (kons knil len vec i)
(if (= i len)
knil
(loop kons
(kons knil (vector-ref vec i))
len vec (+ i 1))))))
(lambda (kons knil len vec)
(loop kons knil len vec 0))))
;;; (%VECTOR-FOLD2+ ...) -> knil'
;;; (KONS ...) -> knil'
(define %vector-fold2+
(letrec ((loop (lambda (kons knil len vectors i)
(if (= i len)
knil
(loop kons
(apply kons knil
(vectors-ref vectors i))
len vectors (+ i 1))))))
(lambda (kons knil len vectors)
(loop kons knil len vectors 0))))
;;; (%VECTOR-MAP! ) -> target
;;; (F ) -> elt'
(define %vector-map1!
(letrec ((loop (lambda (f target vec i)
(if (zero? i)
target
(let ((j (- i 1)))
(vector-set! target j
(f (vector-ref vec j)))
(loop f target vec j))))))
(lambda (f target vec len)
(loop f target vec len))))
;;; (%VECTOR-MAP2+! ) -> target
;;; (F ...) -> elt'
(define %vector-map2+!
(letrec ((loop (lambda (f target vectors i)
(if (zero? i)
target
(let ((j (- i 1)))
(vector-set! target j
(apply f (vectors-ref vectors j)))
(loop f target vectors j))))))
(lambda (f target vectors len)
(loop f target vectors len))))
;;;;;;;;;;;;;;;;;;;;;;;; ***** vector-lib ***** ;;;;;;;;;;;;;;;;;;;;;;;
;;; --------------------
;;; Constructors
;;; (VECTOR-UNFOLD ...) -> vector
;;; (F ...) -> [elt seed' ...]
;;; The fundamental vector constructor. Creates a vector whose
;;; length is LENGTH and iterates across each index K between 0 and
;;; LENGTH, applying F at each iteration to the current index and the
;;; current seeds to receive N+1 values: first, the element to put in
;;; the Kth slot and then N new seeds for the next iteration.
(define (vector-unfold f length . initial-seeds)
(define vec (make-vector length))
(apply vector-unfold! f vec 0 length initial-seeds)
vec)
;;; (VECTOR-UNFOLD! ...) -> vector
;;; (F ...) -> [elt seed' ...]
;;; Like VECTOR-UNFOLD, but unfolds onto an existing vector starting
;;; at up to but not including .
(define vector-unfold!
(letrec ((tabulate! ; Special zero-seed case.
(lambda (f vec i len)
(cond ((< i len)
(vector-set! vec i (f i))
(tabulate! f vec (+ i 1) len)))))
(unfold1! ; Fast path for one seed.
(lambda (f vec i len seed)
(if (< i len)
(receive (elt new-seed)
(f i seed)
(vector-set! vec i elt)
(unfold1! f vec (+ i 1) len new-seed)))))
(unfold2+! ; Slower variant for N seeds.
(lambda (f vec i len seeds)
(if (< i len)
(receive (elt . new-seeds)
(apply f i seeds)
(vector-set! vec i elt)
(unfold2+! f vec (+ i 1) len new-seeds))))))
(lambda (f vec start end . initial-seeds)
(let ((f (check-type procedure? f vector-unfold!))
(start (check-type nonneg-int? start vector-unfold!))
(end (check-type nonneg-int? end vector-unfold!)))
(let ()
(cond ((null? initial-seeds)
(tabulate! f vec start end))
((null? (cdr initial-seeds))
(unfold1! f vec start end (car initial-seeds)))
(else
(unfold2+! f vec start end initial-seeds))))))))
;;; (VECTOR-UNFOLD-RIGHT ...) -> vector
;;; (F ...) -> [seed' ...]
;;; Like VECTOR-UNFOLD, but it generates elements from LENGTH to 0
;;; (still exclusive with LENGTH and inclusive with 0), not 0 to
;;; LENGTH as with VECTOR-UNFOLD.
(define (vector-unfold-right f len . initial-seeds)
(define vec (make-vector len))
(apply vector-unfold-right! f vec 0 len initial-seeds)
vec)
;;; (VECTOR-UNFOLD-RIGHT! ...) -> vector
;;; Like VECTOR-UNFOLD-RIGHT, but unfolds onto an existing vector.
(define (vector-unfold-right! f vec start end . initial-seeds)
(letrec ((tabulate!
(lambda (f vec i)
(cond ((>= i start)
(vector-set! vec i (f i))
(tabulate! f vec (- i 1))))))
(unfold1!
(lambda (f vec i seed)
(if (>= i start)
(receive (elt new-seed)
(f i seed)
(vector-set! vec i elt)
(unfold1! f vec (- i 1) new-seed)))))
(unfold2+!
(lambda (f vec i seeds)
(if (>= i start)
(receive (elt . new-seeds)
(apply f i seeds)
(vector-set! vec i elt)
(unfold2+! f vec (- i 1) new-seeds))))))
(let ((f (check-type procedure? f vector-unfold-right!))
(start (check-type nonneg-int? start vector-unfold-right!))
(end (check-type nonneg-int? end vector-unfold-right!)))
(let ((i (- end 1)))
(cond ((null? initial-seeds)
(tabulate! f vec i))
((null? (cdr initial-seeds))
(unfold1! f vec i (car initial-seeds)))
(else
(unfold2+! f vec i initial-seeds)))))))
;;; (VECTOR-COPY []) -> vector
;;; Create a newly allocated vector containing the elements from the
;;; range [START,END) in VECTOR. START defaults to 0; END defaults
;;; to the length of VECTOR. END may be greater than the length of
;;; VECTOR, in which case the vector is enlarged; if FILL is passed,
;;; the new locations from which there is no respective element in
;;; VECTOR are filled with FILL.
(define (vector-copy vec . args)
(let ((vec (check-type vector? vec vector-copy)))
;; We can't use LET-VECTOR-START+END, because we have one more
;; argument, and we want finer control, too.
;;
;; Olin's implementation of LET*-OPTIONALS would prove useful here:
;; the built-in argument-checks-as-you-go-along produces almost
;; _exactly_ the same code as VECTOR-COPY:PARSE-ARGS.
(receive (start end fill)
(vector-copy:parse-args vec args)
(let ((new-vector (make-vector (- end start) fill)))
(%vector-copy! new-vector 0
vec start
(if (> end (vector-length vec))
(vector-length vec)
end))
new-vector))))
;;; Auxiliary for VECTOR-COPY.
;;; [wdc] Corrected to allow 0 <= start <= (vector-length vec).
(define (vector-copy:parse-args vec args)
(define (parse-args start end n fill)
(let ((start (check-type nonneg-int? start vector-copy))
(end (check-type nonneg-int? end vector-copy)))
(cond ((and (<= 0 start end)
(<= start n))
(values start end fill))
(else
(error "illegal arguments"
`(while calling ,vector-copy)
`(start was ,start)
`(end was ,end)
`(vector was ,vec))))))
(let ((n (vector-length vec)))
(cond ((null? args)
(parse-args 0 n n (unspecified-value)))
((null? (cdr args))
(parse-args (car args) n n (unspecified-value)))
((null? (cddr args))
(parse-args (car args) (cadr args) n (unspecified-value)))
((null? (cdddr args))
(parse-args (car args) (cadr args) n (caddr args)))
(else
(error "too many arguments"
vector-copy
(cdddr args))))))
;;; (VECTOR-REVERSE-COPY []) -> vector
;;; Create a newly allocated vector whose elements are the reversed
;;; sequence of elements between START and END in VECTOR. START's
;;; default is 0; END's default is the length of VECTOR.
(define (vector-reverse-copy vec . maybe-start+end)
(let-vector-start+end vector-reverse-copy vec maybe-start+end
(start end)
(let ((new (make-vector (- end start))))
(%vector-reverse-copy! new 0 vec start end)
new)))
;;; (VECTOR-APPEND ...) -> vector
;;; Append VECTOR ... into a newly allocated vector and return that
;;; new vector.
(define (vector-append . vectors)
(vector-concatenate:aux vectors vector-append))
;;; (VECTOR-CONCATENATE ) -> vector
;;; Concatenate the vectors in VECTOR-LIST. This is equivalent to
;;; (apply vector-append VECTOR-LIST)
;;; but VECTOR-APPEND tends to be implemented in terms of
;;; VECTOR-CONCATENATE, and some Schemes bork when the list to apply
;;; a function to is too long.
;;;
;;; Actually, they're both implemented in terms of an internal routine.
(define (vector-concatenate vector-list)
(vector-concatenate:aux vector-list vector-concatenate))
;;; Auxiliary for VECTOR-APPEND and VECTOR-CONCATENATE
(define vector-concatenate:aux
(letrec ((compute-length
(lambda (vectors len callee)
(if (null? vectors)
len
(let ((vec (check-type vector? (car vectors)
callee)))
(compute-length (cdr vectors)
(+ (vector-length vec) len)
callee)))))
(concatenate!
(lambda (vectors target to)
(if (null? vectors)
target
(let* ((vec1 (car vectors))
(len (vector-length vec1)))
(%vector-copy! target to vec1 0 len)
(concatenate! (cdr vectors) target
(+ to len)))))))
(lambda (vectors callee)
(cond ((null? vectors) ;+++
(make-vector 0))
((null? (cdr vectors)) ;+++
;; Blech, we still have to allocate a new one.
(let* ((vec (check-type vector? (car vectors) callee))
(len (vector-length vec))
(new (make-vector len)))
(%vector-copy! new 0 vec 0 len)
new))
(else
(let ((new-vector
(make-vector (compute-length vectors 0 callee))))
(concatenate! vectors new-vector 0)
new-vector))))))
;;; (VECTOR-APPEND-SUBVECTORS ...) -> vector
;;; Like VECTOR-APPEND but appends subvectors specified by
;;; argument triples.
(define (vector-append-subvectors . args)
;; GATHER-ARGS returns three values: vectors, starts, ends
(define (gather-args args)
(let loop ((args args) (vecs '()) (starts '()) (ends '()))
(if (null? args)
(values (reverse vecs) (reverse starts) (reverse ends))
(loop (cdddr args)
(cons (car args) vecs)
(cons (cadr args) starts)
(cons (caddr args) ends)))))
;; TOTAL-LENGTH computes the length of all subvectors
(define (total-length starts ends)
(let loop ((count 0) (starts starts) (ends ends))
(if (null? starts)
count
(let ((start (car starts)) (end (car ends)))
(loop (+ count (- end start))
(cdr starts)
(cdr ends))))))
;; COPY-EACH! copies each subvector into a result vector
(define (copy-each! result vecs starts ends)
(let loop ((at 0) (vecs vecs) (starts starts) (ends ends))
(if (null? vecs)
result
(let ((vec (car vecs)) (start (car starts)) (end (car ends)))
(%vector-copy! result at vec start end)
(loop (+ at (- end start))
(cdr vecs)
(cdr starts)
(cdr ends))))))
;; put them all together, they spell VECTOR-APPEND-SUBVECTORS
(receive (vecs starts ends) (gather-args args)
(define result (make-vector (total-length starts ends)))
(copy-each! result vecs starts ends)))
;;; --------------------
;;; Predicates
;;; (VECTOR-EMPTY? ) -> boolean
;;; Return #T if VECTOR has zero elements in it, i.e. VECTOR's length
;;; is 0, and #F if not.
(define (vector-empty? vec)
(let ((vec (check-type vector? vec vector-empty?)))
(zero? (vector-length vec))))
;;; (VECTOR= ...) -> boolean
;;; (ELT=? ) -> boolean
;;; Determine vector equality generalized across element comparators.
;;; Vectors A and B are equal iff their lengths are the same and for
;;; each respective elements E_a and E_b (element=? E_a E_b) returns
;;; a true value. ELT=? is always applied to two arguments. Element
;;; comparison must be consistent wtih EQ?; that is, if (eq? E_a E_b)
;;; results in a true value, then (ELEMENT=? E_a E_b) must result in a
;;; true value. This may be exploited to avoid multiple unnecessary
;;; element comparisons. (This implementation does, but does not deal
;;; with the situation that ELEMENT=? is EQ? to avoid more unnecessary
;;; comparisons, but I believe this optimization is probably fairly
;;; insignificant.)
;;;
;;; If the number of vector arguments is zero or one, then #T is
;;; automatically returned. If there are N vector arguments,
;;; VECTOR_1 VECTOR_2 ... VECTOR_N, then VECTOR_1 & VECTOR_2 are
;;; compared; if they are equal, the vectors VECTOR_2 ... VECTOR_N
;;; are compared. The precise order in which ELT=? is applied is not
;;; specified.
(define (vector= elt=? . vectors)
(let ((elt=? (check-type procedure? elt=? vector=)))
(cond ((null? vectors)
#t)
((null? (cdr vectors))
(check-type vector? (car vectors) vector=)
#t)
(else
(let loop ((vecs vectors))
(let ((vec1 (check-type vector? (car vecs) vector=))
(vec2+ (cdr vecs)))
(or (null? vec2+)
(and (binary-vector= elt=? vec1 (car vec2+))
(loop vec2+)))))))))
(define (binary-vector= elt=? vector-a vector-b)
(or (eq? vector-a vector-b) ;+++
(let ((length-a (vector-length vector-a))
(length-b (vector-length vector-b)))
(letrec ((loop (lambda (i)
(or (= i length-a)
(and (< i length-b)
(test (vector-ref vector-a i)
(vector-ref vector-b i)
i)))))
(test (lambda (elt-a elt-b i)
(and (or (eq? elt-a elt-b) ;+++
(elt=? elt-a elt-b))
(loop (+ i 1))))))
(and (= length-a length-b)
(loop 0))))))
;;; --------------------
;;; Selectors
;;; --------------------
;;; Iteration
;;; (VECTOR-FOLD ...) -> knil
;;; (KONS ...) -> knil' ; N vectors -> N+1 args
;;; The fundamental vector iterator. KONS is iterated over each
;;; index in all of the vectors in parallel, stopping at the end of
;;; the shortest; KONS is applied to an argument list of (list I
;;; STATE (vector-ref VEC I) ...), where STATE is the current state
;;; value -- the state value begins with KNIL and becomes whatever
;;; KONS returned at the respective iteration --, and I is the
;;; current index in the iteration. The iteration is strictly left-
;;; to-right.
;;; (vector-fold KONS KNIL (vector E_1 E_2 ... E_N))
;;; <=>
;;; (KONS (... (KONS (KONS KNIL E_1) E_2) ... E_N-1) E_N)
(define (vector-fold kons knil vec . vectors)
(let ((kons (check-type procedure? kons vector-fold))
(vec (check-type vector? vec vector-fold)))
(if (null? vectors)
(%vector-fold1 kons knil (vector-length vec) vec)
(%vector-fold2+ kons knil
(%smallest-length vectors
(vector-length vec)
vector-fold)
(cons vec vectors)))))
;;; (VECTOR-FOLD-RIGHT ...) -> knil
;;; (KONS ...) -> knil' ; N vectors => N+1 args
;;; The fundamental vector recursor. Iterates in parallel across
;;; VECTOR ... right to left, applying KONS to the elements and the
;;; current state value; the state value becomes what KONS returns
;;; at each next iteration. KNIL is the initial state value.
;;; (vector-fold-right KONS KNIL (vector E_1 E_2 ... E_N))
;;; <=>
;;; (KONS (... (KONS (KONS KNIL E_N) E_N-1) ... E_2) E_1)
;;;
;;; Not implemented in terms of a more primitive operations that might
;;; called %VECTOR-FOLD-RIGHT due to the fact that it wouldn't be very
;;; useful elsewhere.
(define vector-fold-right
(letrec ((loop1 (lambda (kons knil vec i)
(if (negative? i)
knil
(loop1 kons (kons knil (vector-ref vec i))
vec
(- i 1)))))
(loop2+ (lambda (kons knil vectors i)
(if (negative? i)
knil
(loop2+ kons
(apply kons knil
(vectors-ref vectors i))
vectors
(- i 1))))))
(lambda (kons knil vec . vectors)
(let ((kons (check-type procedure? kons vector-fold-right))
(vec (check-type vector? vec vector-fold-right)))
(if (null? vectors)
(loop1 kons knil vec (- (vector-length vec) 1))
(loop2+ kons knil (cons vec vectors)
(- (%smallest-length vectors
(vector-length vec)
vector-fold-right)
1)))))))
;;; (VECTOR-MAP ...) -> vector
;;; (F ...) -> value ; N vectors -> N args
;;; Constructs a new vector of the shortest length of the vector
;;; arguments. Each element at index I of the new vector is mapped
;;; from the old vectors by (F I (vector-ref VECTOR I) ...). The
;;; dynamic order of application of F is unspecified.
;;; provided by (rnrs base)
#;(define (vector-map f vec . vectors)
(let ((f (check-type procedure? f vector-map))
(vec (check-type vector? vec vector-map)))
(if (null? vectors)
(let ((len (vector-length vec)))
(%vector-map1! f (make-vector len) vec len))
(let ((len (%smallest-length vectors
(vector-length vec)
vector-map)))
(%vector-map2+! f (make-vector len) (cons vec vectors)
len)))))
;;; (VECTOR-MAP! ...) -> unspecified
;;; (F ...) -> element' ; N vectors -> N args
;;; Similar to VECTOR-MAP, but rather than mapping the new elements
;;; into a new vector, the new mapped elements are destructively
;;; inserted into the first vector. Again, the dynamic order of
;;; application of F is unspecified, so it is dangerous for F to
;;; manipulate the first VECTOR.
(define (vector-map! f vec . vectors)
(let ((f (check-type procedure? f vector-map!))
(vec (check-type vector? vec vector-map!)))
(if (null? vectors)
(%vector-map1! f vec vec (vector-length vec))
(%vector-map2+! f vec (cons vec vectors)
(%smallest-length vectors
(vector-length vec)
vector-map!)))
(unspecified-value)))
;;; (VECTOR-FOR-EACH ...) -> unspecified
;;; (F ...) ; N vectors -> N args
;;; Simple vector iterator: applies F to each index in the range [0,
;;; LENGTH), where LENGTH is the length of the smallest vector
;;; argument passed, and the respective element at that index. In
;;; contrast with VECTOR-MAP, F is reliably applied to each
;;; subsequent elements, starting at index 0 from left to right, in
;;; the vectors.
;;; provided by (rnrs base)
#;(define vector-for-each
(letrec ((for-each1
(lambda (f vec i len)
(cond ((< i len)
(f (vector-ref vec i))
(for-each1 f vec (+ i 1) len)))))
(for-each2+
(lambda (f vecs i len)
(cond ((< i len)
(apply f (vectors-ref vecs i))
(for-each2+ f vecs (+ i 1) len))))))
(lambda (f vec . vectors)
(let ((f (check-type procedure? f vector-for-each))
(vec (check-type vector? vec vector-for-each)))
(if (null? vectors)
(for-each1 f vec 0 (vector-length vec))
(for-each2+ f (cons vec vectors) 0
(%smallest-length vectors
(vector-length vec)
vector-for-each)))))))
;;; (VECTOR-COUNT ...)
;;; -> exact, nonnegative integer
;;; (PREDICATE? ...) ; N vectors -> N args
;;; PREDICATE? is applied element-wise to the elements of VECTOR ...,
;;; and a count is tallied of the number of elements for which a
;;; true value is produced by PREDICATE?. This count is returned.
(define (vector-count pred? vec . vectors)
(let ((pred? (check-type procedure? pred? vector-count))
(vec (check-type vector? vec vector-count)))
(if (null? vectors)
(%vector-fold1 (lambda (count elt)
(if (pred? elt)
(+ count 1)
count))
0
(vector-length vec)
vec)
(%vector-fold2+ (lambda (count . elts)
(if (apply pred? elts)
(+ count 1)
count))
0
(%smallest-length vectors
(vector-length vec)
vector-count)
(cons vec vectors)))))
;;; (VECTOR-CUMULATE )
;;; -> vector
;;; Returns a ly allocated vector with the same length as
;;; . Each element of is set to the result of invoking on
;;; [i-1] and [i], except that for the first call on , the first
;;; argument is . The vector is returned.
(define (vector-cumulate f vec knil)
(let* ((len (vector-length vec))
(result (make-vector len)))
(let loop ((i 0) (left knil))
(if (= i len)
result
(let* ((right (vector-ref vec i)) (r (f left right)))
(vector-set! result i r)
(loop (+ i 1) r))))))
;;; --------------------
;;; Searching
;;; (VECTOR-INDEX ...)
;;; -> exact, nonnegative integer or #F
;;; (PREDICATE? ...) -> boolean ; N vectors -> N args
;;; Search left-to-right across VECTOR ... in parallel, returning the
;;; index of the first set of values VALUE ... such that (PREDICATE?
;;; VALUE ...) returns a true value; if no such set of elements is
;;; reached, return #F.
(define (vector-index pred? vec . vectors)
(vector-index/skip pred? vec vectors vector-index))
;;; (VECTOR-SKIP ...)
;;; -> exact, nonnegative integer or #F
;;; (PREDICATE? ...) -> boolean ; N vectors -> N args
;;; (vector-index (lambda elts (not (apply PREDICATE? elts)))
;;; VECTOR ...)
;;; Like VECTOR-INDEX, but find the index of the first set of values
;;; that do _not_ satisfy PREDICATE?.
(define (vector-skip pred? vec . vectors)
(vector-index/skip (lambda elts (not (apply pred? elts)))
vec vectors
vector-skip))
;;; Auxiliary for VECTOR-INDEX & VECTOR-SKIP
(define vector-index/skip
(letrec ((loop1 (lambda (pred? vec len i)
(cond ((= i len) #f)
((pred? (vector-ref vec i)) i)
(else (loop1 pred? vec len (+ i 1))))))
(loop2+ (lambda (pred? vectors len i)
(cond ((= i len) #f)
((apply pred? (vectors-ref vectors i)) i)
(else (loop2+ pred? vectors len
(+ i 1)))))))
(lambda (pred? vec vectors callee)
(let ((pred? (check-type procedure? pred? callee))
(vec (check-type vector? vec callee)))
(if (null? vectors)
(loop1 pred? vec (vector-length vec) 0)
(loop2+ pred? (cons vec vectors)
(%smallest-length vectors
(vector-length vec)
callee)
0))))))
;;; (VECTOR-INDEX-RIGHT ...)
;;; -> exact, nonnegative integer or #F
;;; (PREDICATE? ...) -> boolean ; N vectors -> N args
;;; Right-to-left variant of VECTOR-INDEX.
(define (vector-index-right pred? vec . vectors)
(vector-index/skip-right pred? vec vectors vector-index-right))
;;; (VECTOR-SKIP-RIGHT ...)
;;; -> exact, nonnegative integer or #F
;;; (PREDICATE? ...) -> boolean ; N vectors -> N args
;;; Right-to-left variant of VECTOR-SKIP.
(define (vector-skip-right pred? vec . vectors)
(vector-index/skip-right (lambda elts (not (apply pred? elts)))
vec vectors
vector-index-right))
(define vector-index/skip-right
(letrec ((loop1 (lambda (pred? vec i)
(cond ((negative? i) #f)
((pred? (vector-ref vec i)) i)
(else (loop1 pred? vec (- i 1))))))
(loop2+ (lambda (pred? vectors i)
(cond ((negative? i) #f)
((apply pred? (vectors-ref vectors i)) i)
(else (loop2+ pred? vectors (- i 1)))))))
(lambda (pred? vec vectors callee)
(let ((pred? (check-type procedure? pred? callee))
(vec (check-type vector? vec callee)))
(if (null? vectors)
(loop1 pred? vec (- (vector-length vec) 1))
(loop2+ pred? (cons vec vectors)
(- (%smallest-length vectors
(vector-length vec)
callee)
1)))))))
;;; (VECTOR-BINARY-SEARCH [])
;;; -> exact, nonnegative integer or #F
;;; (CMP ) -> integer
;;; positive -> VALUE1 > VALUE2
;;; zero -> VALUE1 = VALUE2
;;; negative -> VALUE1 < VALUE2
;;; Perform a binary search through VECTOR for VALUE, comparing each
;;; element to VALUE with CMP.
(define (vector-binary-search vec value cmp . maybe-start+end)
(let ((cmp (check-type procedure? cmp vector-binary-search)))
(let-vector-start+end vector-binary-search vec maybe-start+end
(start end)
(let loop ((start start) (end end) (j #f))
(let ((i (div (+ start end) 2)))
(if (or (= start end) (and j (= i j)))
#f
(let ((comparison
(check-type integer?
(cmp (vector-ref vec i) value)
`(,cmp for ,vector-binary-search))))
(cond ((zero? comparison) i)
((positive? comparison) (loop start i i))
(else (loop i end i))))))))))
;;; (VECTOR-ANY ...) -> value
;;; Apply PRED? to each parallel element in each VECTOR ...; if PRED?
;;; should ever return a true value, immediately stop and return that
;;; value; otherwise, when the shortest vector runs out, return #F.
;;; The iteration and order of application of PRED? across elements
;;; is of the vectors is strictly left-to-right.
(define vector-any
(letrec ((loop1 (lambda (pred? vec i len len-1)
(and (not (= i len))
(if (= i len-1)
(pred? (vector-ref vec i))
(or (pred? (vector-ref vec i))
(loop1 pred? vec (+ i 1)
len len-1))))))
(loop2+ (lambda (pred? vectors i len len-1)
(and (not (= i len))
(if (= i len-1)
(apply pred? (vectors-ref vectors i))
(or (apply pred? (vectors-ref vectors i))
(loop2+ pred? vectors (+ i 1)
len len-1)))))))
(lambda (pred? vec . vectors)
(let ((pred? (check-type procedure? pred? vector-any))
(vec (check-type vector? vec vector-any)))
(if (null? vectors)
(let ((len (vector-length vec)))
(loop1 pred? vec 0 len (- len 1)))
(let ((len (%smallest-length vectors
(vector-length vec)
vector-any)))
(loop2+ pred? (cons vec vectors) 0 len (- len 1))))))))
;;; (VECTOR-EVERY ...) -> value
;;; Apply PRED? to each parallel value in each VECTOR ...; if PRED?
;;; should ever return #F, immediately stop and return #F; otherwise,
;;; if PRED? should return a true value for each element, stopping at
;;; the end of the shortest vector, return the last value that PRED?
;;; returned. In the case that there is an empty vector, return #T.
;;; The iteration and order of application of PRED? across elements
;;; is of the vectors is strictly left-to-right.
(define vector-every
(letrec ((loop1 (lambda (pred? vec i len len-1)
(or (= i len)
(if (= i len-1)
(pred? (vector-ref vec i))
(and (pred? (vector-ref vec i))
(loop1 pred? vec (+ i 1)
len len-1))))))
(loop2+ (lambda (pred? vectors i len len-1)
(or (= i len)
(if (= i len-1)
(apply pred? (vectors-ref vectors i))
(and (apply pred? (vectors-ref vectors i))
(loop2+ pred? vectors (+ i 1)
len len-1)))))))
(lambda (pred? vec . vectors)
(let ((pred? (check-type procedure? pred? vector-every))
(vec (check-type vector? vec vector-every)))
(if (null? vectors)
(let ((len (vector-length vec)))
(loop1 pred? vec 0 len (- len 1)))
(let ((len (%smallest-length vectors
(vector-length vec)
vector-every)))
(loop2+ pred? (cons vec vectors) 0 len (- len 1))))))))
;;; (VECTOR-PARTITION ) -> vector
;;; A vector the same size as is newly allocated and filled with
;;; all the elements of that satisfy in their original
;;; order followed by all the elements that do not satisfy ,
;;; also in their original order.
;;; Two values are returned, the newly allocated vector and the index
;;; of the leftmost element that does not satisfy .
(define (vector-partition pred? vec)
(let* ((len (vector-length vec))
(cnt (vector-count pred? vec))
(result (make-vector len)))
(let loop ((i 0) (yes 0) (no cnt))
(if (= i len)
(values result cnt)
(let ((elem (vector-ref vec i)))
(if (pred? elem)
(begin
(vector-set! result yes elem)
(loop (+ i 1) (+ yes 1) no))
(begin
(vector-set! result no elem)
(loop (+ i 1) yes (+ no 1)))))))))
;;; --------------------
;;; Mutators
;;; (VECTOR-SWAP! ) -> unspecified
;;; Swap the values in the locations at INDEX1 and INDEX2.
(define (vector-swap! vec i j)
(let ((vec (check-type vector? vec vector-swap!)))
(let ((i (check-index vec i vector-swap!))
(j (check-index vec j vector-swap!)))
(let ((x (vector-ref vec i)))
(vector-set! vec i (vector-ref vec j))
(vector-set! vec j x)))))
;;; helper for case-lambda-based versions
(define $check-types
(case-lambda
[(who v start)
(check-type vector? v who)
(check-indices v start 'start (vector-length v) 'end who)]
[(who v start end)
(check-type vector? v who)
(check-indices v start 'start end 'end who)]))
;;; (VECTOR-FILL! []) -> unspecified
;;; [R5RS+] Fill the locations in VECTOR between START, whose default
;;; is 0, and END, whose default is the length of VECTOR, with VALUE.
;;;
;;; This one can probably be made really fast natively.
(define vector-fill!
(let ()
(define $vector-fill!
(lambda (vec value start end)
(do ((i start (+ i 1)))
((= i end))
(vector-set! vec i value))))
(case-lambda
[(vec value) (rnrs:vector-fill! vec value)]
[(vec value start)
($check-types 'vector-fill! vec start)
($vector-fill! vec value start (vector-length vec))]
[(vec value start end)
($check-types 'vector-fill! vec start end)
($vector-fill! vec value start end)])))
;;; (VECTOR-COPY! [])
;;; -> unspecified
;;; Copy the values in the locations in [SSTART,SEND) from SOURCE to
;;; to TARGET, starting at TSTART in TARGET.
;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source).
(define (vector-copy! target tstart source . maybe-sstart+send)
(define (doit! sstart send source-length)
(let ((tstart (check-type nonneg-int? tstart vector-copy!))
(sstart (check-type nonneg-int? sstart vector-copy!))
(send (check-type nonneg-int? send vector-copy!)))
(cond ((and (<= 0 sstart send source-length)
(<= (+ tstart (- send sstart)) (vector-length target)))
(%vector-copy! target tstart source sstart send))
(else
(error "illegal arguments"
`(while calling ,vector-copy!)
`(target was ,target)
`(target-length was ,(vector-length target))
`(tstart was ,tstart)
`(source was ,source)
`(source-length was ,source-length)
`(sstart was ,sstart)
`(send was ,send))))))
(let ((n (vector-length source)))
(cond ((null? maybe-sstart+send)
(doit! 0 n n))
((null? (cdr maybe-sstart+send))
(doit! (car maybe-sstart+send) n n))
((null? (cddr maybe-sstart+send))
(doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n))
(else
(error "too many arguments"
vector-copy!
(cddr maybe-sstart+send))))))
;;; (VECTOR-REVERSE-COPY! [])
;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source).
(define (vector-reverse-copy! target tstart source . maybe-sstart+send)
(define (doit! sstart send source-length)
(let ((tstart (check-type nonneg-int? tstart vector-reverse-copy!))
(sstart (check-type nonneg-int? sstart vector-reverse-copy!))
(send (check-type nonneg-int? send vector-reverse-copy!)))
(cond ((and (eq? target source)
(or (between? sstart tstart send)
(between? tstart sstart
(+ tstart (- send sstart)))))
(error "vector range for self-copying overlaps"
vector-reverse-copy!
`(vector was ,target)
`(tstart was ,tstart)
`(sstart was ,sstart)
`(send was ,send)))
((and (<= 0 sstart send source-length)
(<= (+ tstart (- send sstart)) (vector-length target)))
(%vector-reverse-copy! target tstart source sstart send))
(else
(error "illegal arguments"
`(while calling ,vector-reverse-copy!)
`(target was ,target)
`(target-length was ,(vector-length target))
`(tstart was ,tstart)
`(source was ,source)
`(source-length was ,source-length)
`(sstart was ,sstart)
`(send was ,send))))))
(let ((n (vector-length source)))
(cond ((null? maybe-sstart+send)
(doit! 0 n n))
((null? (cdr maybe-sstart+send))
(doit! (car maybe-sstart+send) n n))
((null? (cddr maybe-sstart+send))
(doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n))
(else
(error "too many arguments"
vector-reverse-copy!
(cddr maybe-sstart+send))))))
;;; (VECTOR-REVERSE! []) -> unspecified
;;; Destructively reverse the contents of the sequence of locations
;;; in VECTOR between START, whose default is 0, and END, whose
;;; default is the length of VECTOR.
(define (vector-reverse! vec . start+end)
(let-vector-start+end vector-reverse! vec start+end
(start end)
(%vector-reverse! vec start end)))
;;; --------------------
;;; Conversion
;;; (VECTOR->LIST []) -> list
;;; [R5RS+] Produce a list containing the elements in the locations
;;; between START, whose default is 0, and END, whose default is the
;;; length of VECTOR, from VECTOR.
(define vector->list
(let ()
(define ($vector->list vec start end)
(do ((i (- end 1) (- i 1))
(result '() (cons (vector-ref vec i) result)))
((< i start) result)))
(case-lambda
[(vec) (rnrs:vector->list vec)]
[(vec start)
($check-types 'vector->list vec start)
($vector->list vec start (vector-length vec))]
[(vec start end)
($check-types 'vector->list vec start end)
($vector->list vec start (vector-length vec))])))
;;; (REVERSE-VECTOR->LIST []) -> list
;;; Produce a list containing the elements in the locations between
;;; START, whose default is 0, and END, whose default is the length
;;; of VECTOR, from VECTOR, in reverse order.
(define (reverse-vector->list vec . maybe-start+end)
(let-vector-start+end reverse-vector->list vec maybe-start+end (start end)
(do ((i start (+ i 1))
(result '() (cons (vector-ref vec i) result)))
((= i end) result))))
;;; (LIST->VECTOR []) -> vector
;;; [R5RS+] Produce a vector containing the elements in LIST, which
;;; must be a proper list, between START, whose default is 0, & END,
;;; whose default is the length of LIST. It is suggested that if the
;;; length of LIST is known in advance, the START and END arguments
;;; be passed, so that LIST->VECTOR need not call LENGTH to determine
;;; the length.
;;;
;;; This implementation diverges on circular lists, unless LENGTH fails
;;; and causes - to fail as well. Given a LENGTH* that computes the
;;; length of a list's cycle, this wouldn't diverge, and would work
;;; great for circular lists.
(define list->vector
(case-lambda
[(lst) (rnrs:list->vector lst)]
[(lst start)
(check-type nonneg-int? start list->vector)
(rnrs:list->vector (list-tail lst start))]
[(lst start end)
(check-type nonneg-int? start list->vector)
(check-type nonneg-int? end list->vector)
;; NB: should be fx
(unless (<= start end) (error 'list->vector "start is greater than end" start end))
(let ([len (- end start)])
(let ([v (make-vector len)])
(let loop ([i 0] [ls (list-tail lst start)])
(unless (= i len)
(unless (pair? ls)
(if (null? ls)
(error 'list->vector "list too short" lst start end)
(error 'list->vector "improper list" lst)))
(vector-set! v i (car ls))
(loop (fx+ i 1) (cdr ls))))
v))]))
;;; (REVERSE-LIST->VECTOR []) -> vector
;;; Produce a vector containing the elements in LIST, which must be a
;;; proper list, between START, whose default is 0, and END, whose
;;; default is the length of LIST, in reverse order. It is suggested
;;; that if the length of LIST is known in advance, the START and END
;;; arguments be passed, so that REVERSE-LIST->VECTOR need not call
;;; LENGTH to determine the the length.
;;;
;;; This also diverges on circular lists unless, again, LENGTH returns
;;; something that makes - bork.
(define (reverse-list->vector lst . maybe-start+end)
(let*-optionals maybe-start+end
((start 0)
(end (length lst))) ; Ugh -- LENGTH
(let ((start (check-type nonneg-int? start reverse-list->vector))
(end (check-type nonneg-int? end reverse-list->vector)))
((lambda (f)
(vector-unfold-right f (- end start) (list-tail lst start)))
(lambda (index l)
(cond ((null? l)
(error "list too short"
`(list was ,lst)
`(attempted end was ,end)
`(while calling ,reverse-list->vector)))
((pair? l)
(values (car l) (cdr l)))
(else
(error "erroneous value"
(list list? lst)
`(while calling ,reverse-list->vector)))))))))
;;; (VECTOR->STRING []) -> string
;;; Produce a string containing the elements in the locations
;;; between START, whose default is 0, and END, whose default is the
;;; length of VECTOR, from VECTOR.
(define (vector->string vec . maybe-start+end)
(let* ((len (vector-length vec))
(start (if (null? maybe-start+end) 0 (car maybe-start+end)))
(end (if (null? maybe-start+end)
len
(if (null? (cdr maybe-start+end)) len (cadr maybe-start+end))))
(size (- end start)))
(define result (make-string size))
(let loop ((at 0) (i start))
(if (= i end)
result
(begin
(string-set! result at (vector-ref vec i))
(loop (+ at 1) (+ i 1)))))))
;;; (STRING->VECTOR []) -> vector
;;; Produce a vector containing the elements in STRING
;;; between START, whose default is 0, & END,
;;; whose default is the length of STRING, from STRING.
(define (string->vector str . maybe-start+end)
(let* ((len (string-length str))
(start (if (null? maybe-start+end) 0 (car maybe-start+end)))
(end (if (null? maybe-start+end)
len
(if (null? (cdr maybe-start+end)) len (cadr maybe-start+end))))
(size (- end start)))
(define result (make-vector size))
(let loop ((at 0) (i start))
(if (= i end)
result
(begin
(vector-set! result at (string-ref str i))
(loop (+ at 1) (+ i 1)))))))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a133/vectors.sls 0000664 0000000 0000000 00000002110 14737542645 0021502 0 ustar 00root root 0000000 0000000 (library (srfi :133 vectors)
(export
;;; * Constructors
vector-unfold vector-unfold-right vector-copy vector-reverse-copy
vector-append vector-concatenate vector-append-subvectors
;;; * Predicates
vector-empty? vector=
;;; * Iteration
vector-fold vector-fold-right vector-map vector-map! vector-for-each
vector-count vector-cumulate
;;; * Searching
vector-index vector-skip vector-index-right vector-skip-right
vector-binary-search vector-any vector-every vector-partition
;;; * Mutators
vector-swap! vector-fill! vector-reverse! vector-copy! vector-reverse-copy!
vector-unfold! vector-unfold-right!
;;; * Conversion
vector->list reverse-vector->list list->vector reverse-list->vector
vector->string string->vector)
(import (rename (rnrs)
(vector-fill! rnrs:vector-fill!)
(vector->list rnrs:vector->list)
(list->vector rnrs:list->vector))
(rnrs mutable-strings)
(srfi private include))
(include/resolve ("srfi" "%3a133") "vectors-impl.scm"))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a133/vectors.sls3a132.sls 0000664 0000000 0000000 00000000000 14737542645 0022750 0 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a134.sls 0000664 0000000 0000000 00000001745 14737542645 0020033 0 ustar 00root root 0000000 0000000 (library (srfi :134)
(export ideque ideque-tabulate ideque-unfold ideque-unfold-right
ideque? ideque-empty? ideque= ideque-any ideque-every
ideque-front ideque-add-front ideque-remove-front
ideque-back ideque-add-back ideque-remove-back
ideque-ref
ideque-take ideque-take-right ideque-drop ideque-drop-right
ideque-split-at
ideque-length ideque-append ideque-reverse
ideque-count ideque-zip
ideque-map ideque-filter-map
ideque-for-each ideque-for-each-right
ideque-fold ideque-fold-right
ideque-append-map
ideque-filter ideque-remove ideque-partition
ideque-find ideque-find-right
ideque-take-while ideque-take-while-right
ideque-drop-while ideque-drop-while-right
ideque-span ideque-break
list->ideque ideque->list
generator->ideque ideque->generator
)
(import (srfi :134 ideques)))
chez-srfi-0.0+git20241031.b424440+dfsg/%3a134/ 0000775 0000000 0000000 00000000000 14737542645 0017301 5 ustar 00root root 0000000 0000000 chez-srfi-0.0+git20241031.b424440+dfsg/%3a134/ideques.sls 0000664 0000000 0000000 00000043347 14737542645 0021476 0 ustar 00root root 0000000 0000000 (library (srfi :134 ideques)
(export ideque ideque-tabulate ideque-unfold ideque-unfold-right
ideque? ideque-empty? ideque= ideque-any ideque-every
ideque-front ideque-add-front ideque-remove-front
ideque-back ideque-add-back ideque-remove-back
ideque-ref
ideque-take ideque-take-right ideque-drop ideque-drop-right
ideque-split-at
ideque-length ideque-append ideque-reverse
ideque-count ideque-zip
ideque-map ideque-filter-map
ideque-for-each ideque-for-each-right
ideque-fold ideque-fold-right
ideque-append-map
ideque-filter ideque-remove ideque-partition
ideque-find ideque-find-right
ideque-take-while ideque-take-while-right
ideque-drop-while ideque-drop-while-right
ideque-span ideque-break
list->ideque ideque->list
generator->ideque ideque->generator
)
(import (except (rnrs) define-record-type remove fold-right)
(only (rnrs r5rs) quotient)
(only (srfi :1) fold-right unfold list= concatenate zip
append-map)
(srfi :9)
(srfi :41)
(srfi :158))
;;; Copyright (c) 2015 Shiro Kawai
;;; Copyright (c) 2022 Wolfgang Corcoran-Mathe
;;;
;;; Permission is hereby granted, free of charge, to any person
;;; obtaining a copy of this software and associated documentation files
;;; (the "Software"), to deal in the Software without restriction,
;;; including without limitation the rights to use, copy, modify, merge,
;;; publish, distribute, sublicense, and/or sell copies of the Software,
;;; and to permit persons to whom the Software is furnished to do so,
;;; subject to the following conditions:
;;;
;;; The above copyright notice and this permission notice shall be
;;; included in all copies or substantial portions of the Software.