source: project/release/5/srfi-1/trunk/srfi-1.scm @ 33194

Last change on this file since 33194 was 33194, checked in by evhan, 4 years ago

srfi-1: Export lset procedures

File size: 56.5 KB
Line 
1;;;; srfi-1.scm - Shivers' reference implementation of SRFI-1
2
3
4; Some things to make it work with CHICKEN: (flw)
5;
6
7(declare
8  (disable-interrupts)
9  (not standard-bindings member assoc))
10
11(module srfi-1
12  (xcons make-list list-tabulate cons* list-copy
13   proper-list? circular-list? dotted-list? not-pair? null-list? list=
14   circular-list length+
15   iota
16   alist-cons alist-copy alist-delete alist-delete!
17   first second third fourth fifth sixth seventh eighth ninth tenth
18   car+cdr
19   take drop
20   take-right drop-right
21   take! drop-right!
22   split-at split-at!
23   last last-pair
24   lset= lset<= lset-adjoin lset-difference lset-difference!
25   lset-xor lset-xor! lset-diff+intersection lset-diff+intersection!
26   lset-intersection lset-intersection! lset-union lset-union!
27   zip unzip1 unzip2 unzip3 unzip4 unzip5
28   count
29   delete-duplicates delete-duplicates!
30   append! append-reverse append-reverse! concatenate concatenate!
31   unfold fold pair-fold reduce
32   unfold-right fold-right pair-fold-right reduce-right
33   append-map append-map! map! pair-for-each filter-map map-in-order
34   assoc member
35   filter partition remove delete
36   filter! partition! remove! delete!
37   find find-tail any every list-index
38   take-while drop-while take-while!
39   span break span! break!)
40
41(import (except scheme member assoc) chicken)
42
43(register-feature! 'srfi-1)
44
45
46;;; SRFI-1 list-processing library                      -*- Scheme -*-
47;;; Reference implementation
48;;;
49;;; Copyright (c) 1998, 1999 by Olin Shivers. You may do as you please with
50;;; this code as long as you do not remove this copyright notice or
51;;; hold me liable for its use. Please send bug reports to shivers@ai.mit.edu.
52;;;     -Olin
53
54;;; This is a library of list- and pair-processing functions. I wrote it after
55;;; carefully considering the functions provided by the libraries found in
56;;; R4RS/R5RS Scheme, MIT Scheme, Gambit, RScheme, MzScheme, slib, Common
57;;; Lisp, Bigloo, guile, T, APL and the SML standard basis. It is a pretty
58;;; rich toolkit, providing a superset of the functionality found in any of
59;;; the various Schemes I considered.
60
61;;; This implementation is intended as a portable reference implementation
62;;; for SRFI-1. See the porting notes below for more information.
63
64;;; Exported:
65;;; xcons tree-copy make-list list-tabulate cons* list-copy
66;;; proper-list? circular-list? dotted-list? not-pair? null-list? list=
67;;; circular-list length+
68;;; iota
69;;; first second third fourth fifth sixth seventh eighth ninth tenth
70;;; car+cdr
71;;; take       drop       
72;;; take-right drop-right
73;;; take!      drop-right!
74;;; split-at   split-at!
75;;; last last-pair
76;;; zip unzip1 unzip2 unzip3 unzip4 unzip5
77;;; count
78;;; append! append-reverse append-reverse! concatenate concatenate!
79;;; unfold       fold       pair-fold       reduce
80;;; unfold-right fold-right pair-fold-right reduce-right
81;;; append-map append-map! map! pair-for-each filter-map map-in-order
82;;; filter  partition  remove
83;;; filter! partition! remove!
84;;; find find-tail any every list-index
85;;; take-while drop-while take-while!
86;;; span break span! break!
87
88;;; In principle, the following R4RS list- and pair-processing procedures
89;;; are also part of this package's exports, although they are not defined
90;;; in this file:
91;;;   Primitives: cons pair? null? car cdr set-car! set-cdr!
92;;;   Non-primitives: list length append reverse cadr ... cddddr list-ref
93;;;                   memq memv assq assv
94;;;   (The non-primitives are defined in this file, but commented out.)
95;;;
96;;; These R4RS procedures have extended definitions in SRFI-1 and are defined
97;;; in this file:
98;;;   map for-each member assoc
99;;;
100;;; The remaining two R4RS list-processing procedures are not included:
101;;;   list-tail (use drop)
102;;;   list? (use proper-list?)
103
104
105;;; A note on recursion and iteration/reversal:
106;;; Many iterative list-processing algorithms naturally compute the elements
107;;; of the answer list in the wrong order (left-to-right or head-to-tail) from
108;;; the order needed to cons them into the proper answer (right-to-left, or
109;;; tail-then-head). One style or idiom of programming these algorithms, then,
110;;; loops, consing up the elements in reverse order, then destructively
111;;; reverses the list at the end of the loop. I do not do this. The natural
112;;; and efficient way to code these algorithms is recursively. This trades off
113;;; intermediate temporary list structure for intermediate temporary stack
114;;; structure. In a stack-based system, this improves cache locality and
115;;; lightens the load on the GC system. Don't stand on your head to iterate!
116;;; Recurse, where natural. Multiple-value returns make this even more
117;;; convenient, when the recursion/iteration has multiple state values.
118
119;;; Porting:
120;;; This is carefully tuned code; do not modify casually.
121;;;   - It is careful to share storage when possible;
122;;;   - Side-effecting code tries not to perform redundant writes.
123;;;
124;;; That said, a port of this library to a specific Scheme system might wish
125;;; to tune this code to exploit particulars of the implementation.
126;;; The single most important compiler-specific optimisation you could make
127;;; to this library would be to add rewrite rules or transforms to:
128;;; - transform applications of n-ary procedures (e.g. LIST=, CONS*, APPEND,
129;;;   LSET-UNION) into multiple applications of a primitive two-argument
130;;;   variant.
131;;; - transform applications of the mapping functions (MAP, FOR-EACH, FOLD,
132;;;   ANY, EVERY) into open-coded loops. The killer here is that these
133;;;   functions are n-ary. Handling the general case is quite inefficient,
134;;;   requiring many intermediate data structures to be allocated and
135;;;   discarded.
136;;; - transform applications of procedures that take optional arguments
137;;;   into calls to variants that do not take optional arguments. This
138;;;   eliminates unnecessary consing and parsing of the rest parameter.
139;;;
140;;; These transforms would provide BIG speedups. In particular, the n-ary
141;;; mapping functions are particularly slow and cons-intensive, and are good
142;;; candidates for tuning. I have coded fast paths for the single-list cases,
143;;; but what you really want to do is exploit the fact that the compiler
144;;; usually knows how many arguments are being passed to a particular
145;;; application of these functions -- they are usually explicitly called, not
146;;; passed around as higher-order values. If you can arrange to have your
147;;; compiler produce custom code or custom linkages based on the number of
148;;; arguments in the call, you can speed these functions up a *lot*. But this
149;;; kind of compiler technology no longer exists in the Scheme world as far as
150;;; I can see.
151;;;
152;;; Note that this code is, of course, dependent upon standard bindings for
153;;; the R5RS procedures -- i.e., it assumes that the variable CAR is bound
154;;; to the procedure that takes the car of a list. If your Scheme
155;;; implementation allows user code to alter the bindings of these procedures
156;;; in a manner that would be visible to these definitions, then there might
157;;; be trouble. You could consider horrible kludgery along the lines of
158;;;    (define fact
159;;;      (let ((= =) (- -) (* *))
160;;;        (letrec ((real-fact (lambda (n)
161;;;                              (if (= n 0) 1 (* n (real-fact (- n 1)))))))
162;;;          real-fact)))
163;;; Or you could consider shifting to a reasonable Scheme system that, say,
164;;; has a module system protecting code from this kind of lossage.
165;;;
166;;; This code does a fair amount of run-time argument checking. If your
167;;; Scheme system has a sophisticated compiler that can eliminate redundant
168;;; error checks, this is no problem. However, if not, these checks incur
169;;; some performance overhead -- and, in a safe Scheme implementation, they
170;;; are in some sense redundant: if we don't check to see that the PROC
171;;; parameter is a procedure, we'll find out anyway three lines later when
172;;; we try to call the value. It's pretty easy to rip all this argument
173;;; checking code out if it's inappropriate for your implementation -- just
174;;; nuke every call to CHECK-ARG.
175;;;
176;;; On the other hand, if you *do* have a sophisticated compiler that will
177;;; actually perform soft-typing and eliminate redundant checks (Rice's systems
178;;; being the only possible candidate of which I'm aware), leaving these checks
179;;; in can *help*, since their presence can be elided in redundant cases,
180;;; and in cases where they are needed, performing the checks early, at
181;;; procedure entry, can "lift" a check out of a loop.
182;;;
183;;; Finally, I have only checked the properties that can portably be checked
184;;; with R5RS Scheme -- and this is not complete. You may wish to alter
185;;; the CHECK-ARG parameter checks to perform extra, implementation-specific
186;;; checks, such as procedure arity for higher-order values.
187;;;
188;;; The code has only these non-R4RS dependencies:
189;;;   A few calls to an ERROR procedure;
190;;;   Uses of the R5RS multiple-value procedure VALUES and the m-v binding
191;;;     RECEIVE macro (which isn't R5RS, but is a trivial macro).
192;;;   Many calls to a parameter-checking procedure check-arg:
193;;;    (define (check-arg pred val caller)
194;;;      (let lp ((val val))
195;;;        (if (pred val) val (lp (error "Bad argument" val pred caller)))))
196;;;   A few uses of the LET-OPTIONAL and :OPTIONAL macros for parsing
197;;;     optional arguments.
198;;;
199;;; Most of these procedures use the NULL-LIST? test to trigger the
200;;; base case in the inner loop or recursion. The NULL-LIST? function
201;;; is defined to be a careful one -- it raises an error if passed a
202;;; non-nil, non-pair value. The spec allows an implementation to use
203;;; a less-careful implementation that simply defines NULL-LIST? to
204;;; be NOT-PAIR?. This would speed up the inner loops of these procedures
205;;; at the expense of having them silently accept dotted lists.
206
207;;; A note on dotted lists:
208;;; I, personally, take the view that the only consistent view of lists
209;;; in Scheme is the view that *everything* is a list -- values such as
210;;; 3 or "foo" or 'bar are simply empty dotted lists. This is due to the
211;;; fact that Scheme actually has no true list type. It has a pair type,
212;;; and there is an *interpretation* of the trees built using this type
213;;; as lists.
214;;;
215;;; I lobbied to have these list-processing procedures hew to this
216;;; view, and accept any value as a list argument. I was overwhelmingly
217;;; overruled during the SRFI discussion phase. So I am inserting this
218;;; text in the reference lib and the SRFI spec as a sort of "minority
219;;; opinion" dissent.
220;;;
221;;; Many of the procedures in this library can be trivially redefined
222;;; to handle dotted lists, just by changing the NULL-LIST? base-case
223;;; check to NOT-PAIR?, meaning that any non-pair value is taken to be
224;;; an empty list. For most of these procedures, that's all that is
225;;; required.
226;;;
227;;; However, we have to do a little more work for some procedures that
228;;; *produce* lists from other lists.  Were we to extend these procedures to
229;;; accept dotted lists, we would have to define how they terminate the lists
230;;; produced as results when passed a dotted list. I designed a coherent set
231;;; of termination rules for these cases; this was posted to the SRFI-1
232;;; discussion list. I additionally wrote an earlier version of this library
233;;; that implemented that spec. It has been discarded during later phases of
234;;; the definition and implementation of this library.
235;;;
236;;; The argument *against* defining these procedures to work on dotted
237;;; lists is that dotted lists are the rare, odd case, and that by
238;;; arranging for the procedures to handle them, we lose error checking
239;;; in the cases where a dotted list is passed by accident -- e.g., when
240;;; the programmer swaps a two arguments to a list-processing function,
241;;; one being a scalar and one being a list. For example,
242;;;     (member '(1 3 5 7 9) 7)
243;;; This would quietly return #f if we extended MEMBER to accept dotted
244;;; lists.
245;;;
246;;; The SRFI discussion record contains more discussion on this topic.
247
248
249;;; Constructors
250;;;;;;;;;;;;;;;;
251
252;;; Occasionally useful as a value to be passed to a fold or other
253;;; higher-order procedure.
254(define (xcons d a) (cons a d))
255
256;;;; Recursively copy every cons.
257;(define (tree-copy x)
258;  (let recur ((x x))
259;    (if (not (pair? x)) x
260;       (cons (recur (car x)) (recur (cdr x))))))
261
262;;; Make a list of length LEN.
263
264(define (make-list len . maybe-elt)
265;  (check-arg (lambda (n) (and (integer? n) (>= n 0))) len make-list)
266  (##sys#check-exact len 'make-list)
267  (let ((elt (cond ((null? maybe-elt) #f) ; Default value
268                   ((null? (cdr maybe-elt)) (car maybe-elt))
269                   (else (##sys#error 'make-list "Too many arguments to MAKE-LIST"
270                                (cons len maybe-elt))))))
271    (do ((i len (fx- i 1))
272         (ans '() (cons elt ans)))
273        ((fx<= i 0) ans))))
274
275
276;(define (list . ans) ans)      ; R4RS
277
278
279;;; Make a list of length LEN. Elt i is (PROC i) for 0 <= i < LEN.
280
281(define (list-tabulate len proc)
282;  (check-arg (lambda (n) (and (integer? n) (>= n 0))) len list-tabulate)
283;  (check-arg procedure? proc list-tabulate)
284  (##sys#check-exact len 'list-tabulate)
285  (do ((i (fx- len 1) (fx- i 1))
286       (ans '() (cons (proc i) ans)))
287      ((fx< i 0) ans)))
288
289;;; (cons* a1 a2 ... an) = (cons a1 (cons a2 (cons ... an)))
290;;; (cons* a1) = a1     (cons* a1 a2 ...) = (cons a1 (cons* a2 ...))
291;;;
292;;; (cons first (unfold not-pair? car cdr rest values))
293
294(define (cons* first . rest)
295  (let recur ((x first) (rest rest))
296    (if (pair? rest)
297        (cons x (recur (car rest) (cdr rest)))
298        x)))
299
300;;; (unfold not-pair? car cdr lis values)
301
302(define (list-copy lis)                         
303  (let recur ((lis lis))                       
304    (if (pair? lis)                             
305        (cons (car lis) (recur (cdr lis)))     
306        lis)))                                 
307
308;;; IOTA count [start step]     (start start+step ... start+(count-1)*step)
309
310(define (iota count . maybe-start+step)
311;  (check-arg integer? count iota)
312  (##sys#check-number count 'iota)
313  (if (< count 0) (##sys#error 'iota "Negative step count" iota count))
314  (let-optionals maybe-start+step ((start 0) ; Olin, I'm tired of fixing your stupid bugs - why didn't
315                                   (step 1) ) ; you use your own macros, then?
316    (##sys#check-number start 'iota)
317    (##sys#check-number step 'iota)
318;    (check-arg number? start iota)
319;    (check-arg number? step iota)
320    (let ((last-val (+ start (* (- count 1) step))))
321      (do ((count count (- count 1))
322           (val last-val (- val step))
323           (ans '() (cons val ans)))
324          ((<= count 0)  ans)))))
325         
326;;; I thought these were lovely, but the public at large did not share my
327;;; enthusiasm...
328;;; :IOTA to            (0 ... to-1)
329;;; :IOTA from to       (from ... to-1)
330;;; :IOTA from to step  (from from+step ...)
331
332;;; IOTA: to            (1 ... to)
333;;; IOTA: from to       (from+1 ... to)
334;;; IOTA: from to step  (from+step from+2step ...)
335
336;(define (##srfi1#parse-iota-args arg1 rest-args proc)
337;  (let ((check (lambda (n) (check-arg integer? n proc))))
338;    (check arg1)
339;    (if (pair? rest-args)
340;       (let ((arg2 (check (car rest-args)))
341;             (rest (cdr rest-args)))
342;         (if (pair? rest)
343;             (let ((arg3 (check (car rest)))
344;                   (rest (cdr rest)))
345;               (if (pair? rest) (error "Too many parameters" proc arg1 rest-args)
346;                   (values arg1 arg2 arg3)))
347;             (values arg1 arg2 1)))
348;       (values 0 arg1 1))))
349;
350;(define (iota: arg1 . rest-args)
351;  (receive (from to step) (##srfi1#parse-iota-args arg1 rest-args iota:)
352;    (let* ((numsteps (floor (/ (- to from) step)))
353;          (last-val (+ from (* step numsteps))))
354;      (if (< numsteps 0) (error "Negative step count" iota: from to step))
355;      (do ((steps-left numsteps (- steps-left 1))
356;          (val last-val (- val step))
357;          (ans '() (cons val ans)))
358;         ((<= steps-left 0) ans)))))
359;
360;
361;(define (:iota arg1 . rest-args)
362;  (receive (from to step) (##srfi1#parse-iota-args arg1 rest-args :iota)
363;    (let* ((numsteps (ceiling (/ (- to from) step)))
364;          (last-val (+ from (* step (- numsteps 1)))))
365;      (if (< numsteps 0) (error "Negative step count" :iota from to step))
366;      (do ((steps-left numsteps (- steps-left 1))
367;          (val last-val (- val step))
368;          (ans '() (cons val ans)))
369;         ((<= steps-left 0) ans)))))
370
371
372
373(define (circular-list val1 . vals)
374  (let ((ans (cons val1 vals)))
375    (set-cdr! (last-pair ans) ans)
376    ans))
377
378;;; <proper-list> ::= ()                        ; Empty proper list
379;;;               |   (cons <x> <proper-list>)  ; Proper-list pair
380;;; Note that this definition rules out circular lists -- and this
381;;; function is required to detect this case and return false.
382
383(define proper-list? list?)
384
385#;(define (proper-list? x)
386  (let lp ((x x) (lag x))
387    (if (pair? x)
388        (let ((x (cdr x)))
389          (if (pair? x)
390              (let ((x   (cdr x))
391                    (lag (cdr lag)))
392                (and (not (eq? x lag)) (lp x lag)))
393              (null? x)))
394        (null? x))))
395
396
397;;; A dotted list is a finite list (possibly of length 0) terminated
398;;; by a non-nil value. Any non-cons, non-nil value (e.g., "foo" or 5)
399;;; is a dotted list of length 0.
400;;;
401;;; <dotted-list> ::= <non-nil,non-pair>        ; Empty dotted list
402;;;               |   (cons <x> <dotted-list>)  ; Proper-list pair
403
404(define (dotted-list? x)
405  (let lp ((x x) (lag x))
406    (if (pair? x)
407        (let ((x (cdr x)))
408          (if (pair? x)
409              (let ((x   (cdr x))
410                    (lag (cdr lag)))
411                (and (not (eq? x lag)) (lp x lag)))
412              (not (null? x))))
413        (not (null? x)))))
414
415(define (circular-list? x)
416  (let lp ((x x) (lag x))
417    (and (pair? x)
418         (let ((x (cdr x)))
419           (and (pair? x)
420                (let ((x   (cdr x))
421                      (lag (cdr lag)))
422                  (or (eq? x lag) (lp x lag))))))))
423
424(define (not-pair? x) (##core#inline "C_i_not_pair_p" x))
425
426;;; This is a legal definition which is fast and sloppy:
427;;;     (define null-list? not-pair?)
428;;; but we'll provide a more careful one:
429(define (null-list? l) (##core#inline "C_i_null_list_p" l))           
430
431(define (list= = . lists)
432  (##sys#check-closure = 'list=)
433  (or (null? lists) ; special case
434      (let lp1 ((list-a (car lists)) (others (cdr lists)))
435        (or (null? others)
436            (let ((list-b (car others))
437                  (others (cdr others)))
438              (if (eq? list-a list-b)   ; EQ? => LIST=
439                  (lp1 list-b others)
440                  (let lp2 ((la list-a) (lb list-b))
441                    (if (null-list? la)
442                        (and (null-list? lb)
443                             (lp1 list-b others))
444                        (and (not (null-list? lb))
445                             (= (car la) (car lb))
446                             (lp2 (cdr la) (cdr lb)))))))))))
447                       
448
449
450;;; R4RS, so commented out.
451;(define (length x)                     ; LENGTH may diverge or
452;  (let lp ((x x) (len 0))              ; raise an error if X is
453;    (if (pair? x)                      ; a circular list. This version
454;        (lp (cdr x) (+ len 1))         ; diverges.
455;        len)))
456
457(define (length+ x)                     ; Returns #f if X is circular.
458  (let lp ((x x) (lag x) (len 0))
459    (if (pair? x)
460        (let ((x (cdr x))
461              (len (fx+ len 1)))
462          (if (pair? x)
463              (let ((x   (cdr x))
464                    (lag (cdr lag))
465                    (len (fx+ len 1)))
466                (and (not (eq? x lag)) (lp x lag len)))
467              len))
468        len)))
469
470(define (zip list1 . more-lists) (apply map list list1 more-lists))
471
472
473;;; Selectors
474;;;;;;;;;;;;;
475
476;;; R4RS non-primitives:
477;(define (caar   x) (car (car x)))
478;(define (cadr   x) (car (cdr x)))
479;(define (cdar   x) (cdr (car x)))
480;(define (cddr   x) (cdr (cdr x)))
481;
482;(define (caaar  x) (caar (car x)))
483;(define (caadr  x) (caar (cdr x)))
484;(define (cadar  x) (cadr (car x)))
485;(define (caddr  x) (cadr (cdr x)))
486;(define (cdaar  x) (cdar (car x)))
487;(define (cdadr  x) (cdar (cdr x)))
488;(define (cddar  x) (cddr (car x)))
489;(define (cdddr  x) (cddr (cdr x)))
490;
491;(define (caaaar x) (caaar (car x)))
492;(define (caaadr x) (caaar (cdr x)))
493;(define (caadar x) (caadr (car x)))
494;(define (caaddr x) (caadr (cdr x)))
495;(define (cadaar x) (cadar (car x)))
496;(define (cadadr x) (cadar (cdr x)))
497;(define (caddar x) (caddr (car x)))
498;(define (cadddr x) (caddr (cdr x)))
499;(define (cdaaar x) (cdaar (car x)))
500;(define (cdaadr x) (cdaar (cdr x)))
501;(define (cdadar x) (cdadr (car x)))
502;(define (cdaddr x) (cdadr (cdr x)))
503;(define (cddaar x) (cddar (car x)))
504;(define (cddadr x) (cddar (cdr x)))
505;(define (cdddar x) (cdddr (car x)))
506;(define (cddddr x) (cdddr (cdr x)))
507
508
509(define first  car)
510(define second cadr)
511(define third  caddr)
512(define fourth cadddr)
513(define (fifth   x) (car    (cddddr x)))
514(define (sixth   x) (cadr   (cddddr x)))
515(define (seventh x) (caddr  (cddddr x)))
516(define (eighth  x) (cadddr (cddddr x)))
517(define (ninth   x) (car  (cddddr (cddddr x))))
518(define (tenth   x) (cadr (cddddr (cddddr x))))
519
520(define (car+cdr pair)
521  (##sys#check-pair pair 'car+cdr)
522  (values (##sys#slot pair 0) (##sys#slot pair 1)) )
523
524;;; take & drop
525
526(define (take lis k)
527  (##sys#check-exact k 'take)
528;  (check-arg integer? k take)
529  (let recur ((lis lis) (k k))
530    (if (eq? 0 k) '()
531        (cons (car lis)
532              (recur (cdr lis) (fx- k 1))))))
533
534(define (drop lis k)
535  (##sys#check-exact k 'drop)
536;  (check-arg integer? k drop)
537  (let iter ((lis lis) (k k))
538    (if (eq? 0 k) lis (iter (cdr lis) (fx- k 1)))))
539
540(define (take! lis k)
541  (##sys#check-exact k 'take!)
542;  (check-arg integer? k take!)
543  (if (eq? 0 k) '()
544      (begin (set-cdr! (drop lis (fx- k 1)) '())
545             lis)))
546
547;;; TAKE-RIGHT and DROP-RIGHT work by getting two pointers into the list,
548;;; off by K, then chasing down the list until the lead pointer falls off
549;;; the end.
550
551(define (take-right lis k)
552;  (check-arg integer? k take-right)
553  (let lp ((lag lis)  (lead (drop lis k)))
554    (if (pair? lead)
555        (lp (cdr lag) (cdr lead))
556        lag)))
557
558(define (drop-right lis k)
559;  (check-arg integer? k drop-right)
560  (let recur ((lag lis) (lead (drop lis k)))
561    (if (pair? lead)
562        (cons (car lag) (recur (cdr lag) (cdr lead)))
563        '())))
564
565;;; In this function, LEAD is actually K+1 ahead of LAG. This lets
566;;; us stop LAG one step early, in time to smash its cdr to ().
567(define (drop-right! lis k)
568;  (check-arg integer? k drop-right!)
569  (let ((lead (drop lis k)))
570    (if (pair? lead)
571
572        (let lp ((lag lis)  (lead (cdr lead)))  ; Standard case
573          (if (pair? lead)
574              (lp (cdr lag) (cdr lead))
575              (begin (set-cdr! lag '())
576                     lis)))
577
578        '())))  ; Special case dropping everything -- no cons to side-effect.
579
580;(define (list-ref lis i) (car (drop lis i)))   ; R4RS
581
582;;; These use the APL convention, whereby negative indices mean
583;;; "from the right." I liked them, but they didn't win over the
584;;; SRFI reviewers.
585;;; K >= 0: Take and drop  K elts from the front of the list.
586;;; K <= 0: Take and drop -K elts from the end   of the list.
587
588;(define (take lis k)
589;  (check-arg integer? k take)
590;  (if (negative? k)
591;      (list-tail lis (+ k (length lis)))
592;      (let recur ((lis lis) (k k))
593;       (if (zero? k) '()
594;           (cons (car lis)
595;                 (recur (cdr lis) (- k 1)))))))
596;
597;(define (drop lis k)
598;  (check-arg integer? k drop)
599;  (if (negative? k)
600;      (let recur ((lis lis) (nelts (+ k (length lis))))
601;       (if (zero? nelts) '()
602;           (cons (car lis)
603;                 (recur (cdr lis) (- nelts 1)))))
604;      (list-tail lis k)))
605;
606;
607;(define (take! lis k)
608;  (check-arg integer? k take!)
609;  (cond ((zero? k) '())
610;       ((positive? k)
611;        (set-cdr! (list-tail lis (- k 1)) '())
612;        lis)
613;       (else (list-tail lis (+ k (length lis))))))
614;
615;(define (drop! lis k)
616;  (check-arg integer? k drop!)
617;  (if (negative? k)
618;      (let ((nelts (+ k (length lis))))
619;       (if (zero? nelts) '()
620;           (begin (set-cdr! (list-tail lis (- nelts 1)) '())
621;                  lis)))
622;      (list-tail lis k)))
623
624(define (split-at x k)
625  (##sys#check-exact k 'split-at)
626;  (check-arg integer? k split-at)
627  (let recur ((lis x) (k k))
628    (if (eq? 0 k) (values '() lis)
629        (receive (prefix suffix) (recur (cdr lis) (fx- k 1))
630          (values (cons (car lis) prefix) suffix)))))
631
632(define (split-at! x k)
633  (##sys#check-exact k 'split-at!)
634;  (check-arg integer? k split-at!)
635  (if (eq? 0 k) (values '() x)
636      (let* ((prev (drop x (fx- k 1)))
637             (suffix (cdr prev)))
638        (set-cdr! prev '())
639        (values x suffix))))
640
641
642(define (last lis) (car (last-pair lis)))
643
644(define (last-pair lis)
645;  (check-arg pair? lis last-pair)
646  (let lp ((lis lis))
647    (let ((tail (cdr lis)))
648      (if (pair? tail) (lp tail) lis))))
649
650
651;;; Unzippers -- 1 through 5
652;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
653
654(define (unzip1 lis) (map car lis))
655
656(define (unzip2 lis)
657  (let recur ((lis lis))
658    (if (null-list? lis) (values lis lis)       ; Use NOT-PAIR? to handle
659        (let ((elt (car lis)))                  ; dotted lists.
660          (receive (a b) (recur (cdr lis))
661            (values (cons (car  elt) a)
662                    (cons (cadr elt) b)))))))
663
664(define (unzip3 lis)
665  (let recur ((lis lis))
666    (if (null-list? lis) (values lis lis lis)
667        (let ((elt (car lis)))
668          (receive (a b c) (recur (cdr lis))
669            (values (cons (car   elt) a)
670                    (cons (cadr  elt) b)
671                    (cons (caddr elt) c)))))))
672
673(define (unzip4 lis)
674  (let recur ((lis lis))
675    (if (null-list? lis) (values lis lis lis lis)
676        (let ((elt (car lis)))
677          (receive (a b c d) (recur (cdr lis))
678            (values (cons (car    elt) a)
679                    (cons (cadr   elt) b)
680                    (cons (caddr  elt) c)
681                    (cons (cadddr elt) d)))))))
682
683(define (unzip5 lis)
684  (let recur ((lis lis))
685    (if (null-list? lis) (values lis lis lis lis lis)
686        (let ((elt (car lis)))
687          (receive (a b c d e) (recur (cdr lis))
688            (values (cons (car     elt) a)
689                    (cons (cadr    elt) b)
690                    (cons (caddr   elt) c)
691                    (cons (cadddr  elt) d)
692                    (cons (car (cddddr  elt)) e)))))))
693
694
695;;; append! append-reverse append-reverse! concatenate concatenate!
696;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
697
698(define (append! . lists)
699  ;; First, scan through lists looking for a non-empty one.
700  (let lp ((lists lists) (prev '()))
701    (if (not (pair? lists)) prev
702        (let ((first (car lists))
703              (rest (cdr lists)))
704          (if (not (pair? first)) (lp rest first)
705
706              ;; Now, do the splicing.
707              (let lp2 ((tail-cons (last-pair first))
708                        (rest rest))
709                (if (pair? rest)
710                    (let ((next (car rest))
711                          (rest (cdr rest)))
712                      (set-cdr! tail-cons next)
713                      (lp2 (if (pair? next) (last-pair next) tail-cons)
714                           rest))
715                    first)))))))
716
717;;; APPEND is R4RS.
718;(define (append . lists)
719;  (if (pair? lists)
720;      (let recur ((list1 (car lists)) (lists (cdr lists)))
721;        (if (pair? lists)
722;            (let ((tail (recur (car lists) (cdr lists))))
723;              (fold-right cons tail list1)) ; Append LIST1 & TAIL.
724;            list1))
725;      '()))
726
727;(define (append-reverse rev-head tail) (fold cons tail rev-head))
728
729;(define (append-reverse! rev-head tail)
730;  (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair)
731;             tail
732;             rev-head))
733
734;;; Hand-inline the FOLD and PAIR-FOLD ops for speed.
735
736(define (append-reverse rev-head tail)
737  (let lp ((rev-head rev-head) (tail tail))
738    (if (null-list? rev-head) tail
739        (lp (cdr rev-head) (cons (car rev-head) tail)))))
740
741(define (append-reverse! rev-head tail)
742  (let lp ((rev-head rev-head) (tail tail))
743    (if (null-list? rev-head) tail
744        (let ((next-rev (cdr rev-head)))
745          (set-cdr! rev-head tail)
746          (lp next-rev rev-head)))))
747
748
749(define (concatenate  lists) (reduce-right append  '() lists))
750(define (concatenate! lists) (reduce-right append! '() lists))
751
752;;; Fold/map internal utilities
753;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
754;;; These little internal utilities are used by the general
755;;; fold & mapper funs for the n-ary cases . It'd be nice if they got inlined.
756;;; One the other hand, the n-ary cases are painfully inefficient as it is.
757;;; An aggressive implementation should simply re-write these functions
758;;; for raw efficiency; I have written them for as much clarity, portability,
759;;; and simplicity as can be achieved.
760;;;
761;;; I use the dreaded call/cc to do local aborts. A good compiler could
762;;; handle this with extreme efficiency. An implementation that provides
763;;; a one-shot, non-persistent continuation grabber could help the compiler
764;;; out by using that in place of the call/cc's in these routines.
765;;;
766;;; These functions have funky definitions that are precisely tuned to
767;;; the needs of the fold/map procs -- for example, to minimize the number
768;;; of times the argument lists need to be examined.
769
770;;; Return (map cdr lists).
771;;; However, if any element of LISTS is empty, just abort and return '().
772(define (##srfi1#cdrs lists)
773  (##sys#call-with-current-continuation
774    (lambda (abort)
775      (let recur ((lists lists))
776        (if (pair? lists)
777            (let ((lis (car lists)))
778              (if (null-list? lis) (abort '())
779                  (cons (cdr lis) (recur (cdr lists)))))
780            '())))))
781
782(define (##srfi1#cars+ lists last-elt)  ; (append! (##sys#map car lists) (list last-elt))
783  (let recur ((lists lists))
784    (if (pair? lists) (cons (caar lists) (recur (cdr lists))) (list last-elt))))
785
786;;; LISTS is a (not very long) non-empty list of lists.
787;;; Return two lists: the cars & the cdrs of the lists.
788;;; However, if any of the lists is empty, just abort and return [() ()].
789
790(define (##srfi1#cars+cdrs lists)
791  (##sys#call-with-current-continuation
792    (lambda (abort)
793      (let recur ((lists lists))
794        (if (pair? lists)
795            (receive (list other-lists) (car+cdr lists)
796              (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out
797                  (receive (a d) (car+cdr list)
798                    (receive (cars cdrs) (recur other-lists)
799                      (values (cons a cars) (cons d cdrs))))))
800            (values '() '()))))))
801
802;;; Like ##srfi1#CARS+CDRS, but we pass in a final elt tacked onto the end of the
803;;; cars list. What a hack.
804(define (##srfi1#cars+cdrs+ lists cars-final)
805  (##sys#call-with-current-continuation
806    (lambda (abort)
807      (let recur ((lists lists))
808        (if (pair? lists)
809            (receive (list other-lists) (car+cdr lists)
810              (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out
811                  (receive (a d) (car+cdr list)
812                    (receive (cars cdrs) (recur other-lists)
813                      (values (cons a cars) (cons d cdrs))))))
814            (values (list cars-final) '()))))))
815
816;;; Like ##srfi1#CARS+CDRS, but blow up if any list is empty.
817(define (##srfi1#cars+cdrs/no-test lists)
818  (let recur ((lists lists))
819    (if (pair? lists)
820        (receive (list other-lists) (car+cdr lists)
821          (receive (a d) (car+cdr list)
822            (receive (cars cdrs) (recur other-lists)
823              (values (cons a cars) (cons d cdrs)))))
824        (values '() '()))))
825
826
827;;; count
828;;;;;;;;;
829(define (count pred list1 . lists)
830;  (check-arg procedure? pred count)
831  (if (pair? lists)
832
833      ;; N-ary case
834      (let lp ((list1 list1) (lists lists) (i 0))
835        (if (null-list? list1) i
836            (receive (as ds) (##srfi1#cars+cdrs lists)
837              (if (null? as) i
838                  (lp (cdr list1) ds
839                      (if (apply pred (car list1) as) (fx+ i 1) i))))))
840
841      ;; Fast path
842      (let lp ((lis list1) (i 0))
843        (if (null-list? lis) i
844            (lp (cdr lis) (if (pred (car lis)) (fx+ i 1) i))))))
845
846
847;;; fold/unfold
848;;;;;;;;;;;;;;;
849
850(define (unfold-right p f g seed . maybe-tail)
851;  (check-arg procedure? p unfold-right)
852;  (check-arg procedure? f unfold-right)
853;  (check-arg procedure? g unfold-right)
854  (let lp ((seed seed) (ans (optional maybe-tail '())))
855    (if (p seed) ans
856        (lp (g seed)
857            (cons (f seed) ans)))))
858
859
860(define (unfold p f g seed . maybe-tail-gen)
861;  (check-arg procedure? p unfold)
862;  (check-arg procedure? f unfold)
863;  (check-arg procedure? g unfold)
864  (if (pair? maybe-tail-gen)
865
866      (let ((tail-gen (car maybe-tail-gen)))
867        (if (pair? (cdr maybe-tail-gen))
868            (apply error "Too many arguments" unfold p f g seed maybe-tail-gen)
869
870            (let recur ((seed seed))
871              (if (p seed) (tail-gen seed)
872                  (cons (f seed) (recur (g seed)))))))
873
874      (let recur ((seed seed))
875        (if (p seed) '()
876            (cons (f seed) (recur (g seed)))))))
877     
878
879(define (fold kons knil lis1 . lists)
880;  (check-arg procedure? kons fold)
881  (if (pair? lists)
882      (let lp ((lists (cons lis1 lists)) (ans knil))    ; N-ary case
883        (receive (cars+ans cdrs) (##srfi1#cars+cdrs+ lists ans)
884          (if (null? cars+ans) ans ; Done.
885              (lp cdrs (apply kons cars+ans)))))
886           
887      (let lp ((lis lis1) (ans knil))                   ; Fast path
888        (if (null-list? lis) ans
889            (lp (cdr lis) (kons (car lis) ans))))))
890
891
892(define (fold-right kons knil lis1 . lists)
893;  (check-arg procedure? kons fold-right)
894  (if (pair? lists)
895      (let recur ((lists (cons lis1 lists)))            ; N-ary case
896        (let ((cdrs (##srfi1#cdrs lists)))
897          (if (null? cdrs) knil
898              (apply kons (##srfi1#cars+ lists (recur cdrs))))))
899
900      (let recur ((lis lis1))                           ; Fast path
901        (if (null-list? lis) knil
902            (let ((head (car lis)))
903              (kons head (recur (cdr lis))))))))
904
905
906(define (pair-fold-right f zero lis1 . lists)
907;  (check-arg procedure? f pair-fold-right)
908  (if (pair? lists)
909      (let recur ((lists (cons lis1 lists)))            ; N-ary case
910        (let ((cdrs (##srfi1#cdrs lists)))
911          (if (null? cdrs) zero
912              (apply f (append! lists (list (recur cdrs)))))))
913
914      (let recur ((lis lis1))                           ; Fast path
915        (if (null-list? lis) zero (f lis (recur (cdr lis)))))))
916
917(define (pair-fold f zero lis1 . lists)
918;  (check-arg procedure? f pair-fold)
919  (if (pair? lists)
920      (let lp ((lists (cons lis1 lists)) (ans zero))    ; N-ary case
921        (let ((tails (##srfi1#cdrs lists)))
922          (if (null? tails) ans
923              (lp tails (apply f (append! lists (list ans)))))))
924
925      (let lp ((lis lis1) (ans zero))
926        (if (null-list? lis) ans
927            (let ((tail (cdr lis)))             ; Grab the cdr now,
928              (lp tail (f lis ans)))))))        ; in case F SET-CDR!s LIS.
929     
930
931;;; REDUCE and REDUCE-RIGHT only use RIDENTITY in the empty-list case.
932;;; These cannot meaningfully be n-ary.
933
934(define (reduce f ridentity lis)
935;  (check-arg procedure? f reduce)
936  (if (null-list? lis) ridentity
937      (fold f (car lis) (cdr lis))))
938
939(define (reduce-right f ridentity lis)
940;  (check-arg procedure? f reduce-right)
941  (if (null-list? lis) ridentity
942      (let recur ((head (car lis)) (lis (cdr lis)))
943        (if (pair? lis)
944            (f head (recur (car lis) (cdr lis)))
945            head))))
946
947
948
949;;; Mappers: append-map append-map! pair-for-each map! filter-map map-in-order
950;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
951
952(define (append-map f lis1 . lists)
953  (##srfi1#really-append-map append-map  append  f lis1 lists))
954(define (append-map! f lis1 . lists) 
955  (##srfi1#really-append-map append-map! append! f lis1 lists))
956
957(define (##srfi1#really-append-map who appender f lis1 lists)
958;  (check-arg procedure? f who)
959  (if (pair? lists)
960      (receive (cars cdrs) (##srfi1#cars+cdrs (cons lis1 lists))
961        (if (null? cars) '()
962            (let recur ((cars cars) (cdrs cdrs))
963              (let ((vals (apply f cars)))
964                (receive (cars2 cdrs2) (##srfi1#cars+cdrs cdrs)
965                  (if (null? cars2) vals
966                      (appender vals (recur cars2 cdrs2))))))))
967
968      ;; Fast path
969      (if (null-list? lis1) '()
970          (let recur ((elt (car lis1)) (rest (cdr lis1)))
971            (let ((vals (f elt)))
972              (if (null-list? rest) vals
973                  (appender vals (recur (car rest) (cdr rest)))))))))
974
975
976(define (pair-for-each proc lis1 . lists)
977;  (check-arg procedure? proc pair-for-each)
978  (if (pair? lists)
979
980      (let lp ((lists (cons lis1 lists)))
981        (let ((tails (##srfi1#cdrs lists)))
982          (if (pair? tails)
983              (begin (apply proc lists)
984                     (lp tails)))))
985
986      ;; Fast path.
987      (let lp ((lis lis1))
988        (if (not (null-list? lis))
989            (let ((tail (cdr lis)))     ; Grab the cdr now,
990              (proc lis)                ; in case PROC SET-CDR!s LIS.
991              (lp tail))))))
992
993;;; We stop when LIS1 runs out, not when any list runs out.
994(define (map! f lis1 . lists)
995;  (check-arg procedure? f map!)
996  (if (pair? lists)
997      (let lp ((lis1 lis1) (lists lists))
998        (if (not (null-list? lis1))
999            (receive (heads tails) (##srfi1#cars+cdrs/no-test lists)
1000              (set-car! lis1 (apply f (car lis1) heads))
1001              (lp (cdr lis1) tails))))
1002
1003      ;; Fast path.
1004      (pair-for-each (lambda (pair) (set-car! pair (f (car pair)))) lis1))
1005  lis1)
1006
1007
1008;;; Map F across L, and save up all the non-false results.
1009(define (filter-map f lis1 . lists)
1010;  (check-arg procedure? f filter-map)
1011  (if (pair? lists)
1012      (let recur ((lists (cons lis1 lists)))
1013        (receive (cars cdrs) (##srfi1#cars+cdrs lists)
1014          (if (pair? cars)
1015              (cond ((apply f cars) => (lambda (x) (cons x (recur cdrs))))
1016                    (else (recur cdrs))) ; Tail call in this arm.
1017              '())))
1018           
1019      ;; Fast path.
1020      (let recur ((lis lis1))
1021        (if (null-list? lis) lis
1022            (let ((tail (recur (cdr lis))))
1023              (cond ((f (car lis)) => (lambda (x) (cons x tail)))
1024                    (else tail)))))))
1025
1026
1027;;; Map F across lists, guaranteeing to go left-to-right.
1028;;; NOTE: Some implementations of R5RS MAP are compliant with this spec;
1029;;; in which case this procedure may simply be defined as a synonym for MAP.
1030
1031(define (map-in-order f lis1 . lists)
1032;  (check-arg procedure? f map-in-order)
1033  (if (pair? lists)
1034      (let recur ((lists (cons lis1 lists)))
1035        (receive (cars cdrs) (##srfi1#cars+cdrs lists)
1036          (if (pair? cars)
1037              (let ((x (apply f cars)))         ; Do head first,
1038                (cons x (recur cdrs)))          ; then tail.
1039              '())))
1040           
1041      ;; Fast path.
1042      (let recur ((lis lis1))
1043        (if (null-list? lis) lis
1044            (let ((tail (cdr lis))
1045                  (x (f (car lis))))            ; Do head first,
1046              (cons x (recur tail)))))))        ; then tail.
1047
1048
1049;;; We extend MAP to handle arguments of unequal length.
1050;(define map map-in-order)     
1051
1052
1053;;; filter, remove, partition
1054;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1055;;; FILTER, REMOVE, PARTITION and their destructive counterparts do not
1056;;; disorder the elements of their argument.
1057
1058;; This FILTER shares the longest tail of L that has no deleted elements.
1059;; If Scheme had multi-continuation calls, they could be made more efficient.
1060
1061(define (filter pred lis)                       ; Sleazing with EQ? makes this
1062;  (check-arg procedure? pred filter)           ; one faster.
1063  (let recur ((lis lis))               
1064    (if (null-list? lis) lis                    ; Use NOT-PAIR? to handle dotted lists.
1065        (let ((head (car lis))
1066              (tail (cdr lis)))
1067          (if (pred head)
1068              (let ((new-tail (recur tail)))    ; Replicate the RECUR call so
1069                (if (eq? tail new-tail) lis
1070                    (cons head new-tail)))
1071              (recur tail))))))                 ; this one can be a tail call.
1072
1073
1074;;; Another version that shares longest tail.
1075;(define (filter pred lis)
1076;  (receive (ans no-del?)
1077;      ;; (recur l) returns L with (pred x) values filtered.
1078;      ;; It also returns a flag NO-DEL? if the returned value
1079;      ;; is EQ? to L, i.e. if it didn't have to delete anything.
1080;      (let recur ((l l))
1081;       (if (null-list? l) (values l #t)
1082;           (let ((x  (car l))
1083;                 (tl (cdr l)))
1084;             (if (pred x)
1085;                 (receive (ans no-del?) (recur tl)
1086;                   (if no-del?
1087;                       (values l #t)
1088;                       (values (cons x ans) #f)))
1089;                 (receive (ans no-del?) (recur tl) ; Delete X.
1090;                   (values ans #f))))))
1091;    ans))
1092
1093
1094
1095;(define (filter! pred lis)                     ; Things are much simpler
1096;  (let recur ((lis lis))                       ; if you are willing to
1097;    (if (pair? lis)                            ; push N stack frames & do N
1098;        (cond ((pred (car lis))                ; SET-CDR! writes, where N is
1099;               (set-cdr! lis (recur (cdr lis))); the length of the answer.
1100;               lis)                           
1101;              (else (recur (cdr lis))))
1102;        lis)))
1103
1104
1105;;; This implementation of FILTER!
1106;;; - doesn't cons, and uses no stack;
1107;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are
1108;;;   usually expensive on modern machines, and can be extremely expensive on
1109;;;   modern Schemes (e.g., ones that have generational GC's).
1110;;; It just zips down contiguous runs of in and out elts in LIS doing the
1111;;; minimal number of SET-CDR!s to splice the tail of one run of ins to the
1112;;; beginning of the next.
1113
1114(define (filter! pred lis)
1115;  (check-arg procedure? pred filter!)
1116  (let lp ((ans lis))
1117    (cond ((null-list? ans)       ans)                  ; Scan looking for
1118          ((not (pred (car ans))) (lp (cdr ans)))       ; first cons of result.
1119
1120          ;; ANS is the eventual answer.
1121          ;; SCAN-IN: (CDR PREV) = LIS and (CAR PREV) satisfies PRED.
1122          ;;          Scan over a contiguous segment of the list that
1123          ;;          satisfies PRED.
1124          ;; SCAN-OUT: (CAR PREV) satisfies PRED. Scan over a contiguous
1125          ;;           segment of the list that *doesn't* satisfy PRED.
1126          ;;           When the segment ends, patch in a link from PREV
1127          ;;           to the start of the next good segment, and jump to
1128          ;;           SCAN-IN.
1129          (else (letrec ((scan-in (lambda (prev lis)
1130                                    (if (pair? lis)
1131                                        (if (pred (car lis))
1132                                            (scan-in lis (cdr lis))
1133                                            (scan-out prev (cdr lis))))))
1134                         (scan-out (lambda (prev lis)
1135                                     (let lp ((lis lis))
1136                                       (if (pair? lis)
1137                                           (if (pred (car lis))
1138                                               (begin (set-cdr! prev lis)
1139                                                      (scan-in lis (cdr lis)))
1140                                               (lp (cdr lis)))
1141                                           (set-cdr! prev lis))))))
1142                  (scan-in ans (cdr ans))
1143                  ans)))))
1144
1145
1146;;; This version does not share common tails like the reference impl does.
1147;;; Kindly suggested by Joerg Wittenberger on 20-05-2013.
1148
1149(define (partition pred lst)
1150;  (check-arg procedure? pred partition)
1151  (let ((t (cons #f '()))
1152        (f (cons #f '())))
1153    (let ((tl t) (fl f))
1154      (do ((lst lst (cdr lst)))
1155          ((null? lst) (values (cdr t) (cdr f)))
1156        (let ((elt (car lst)))
1157          (if (pred elt)
1158              (let ((p (cons elt (cdr tl))))
1159                (set-cdr! tl p)
1160                (set! tl p))
1161              (let ((p (cons elt (cdr fl))))
1162                (set-cdr! fl p)
1163                (set! fl p))))))))
1164
1165
1166;(define (partition! pred lis)                  ; Things are much simpler
1167;  (let recur ((lis lis))                       ; if you are willing to
1168;    (if (null-list? lis) (values lis lis)      ; push N stack frames & do N
1169;        (let ((elt (car lis)))                 ; SET-CDR! writes, where N is
1170;          (receive (in out) (recur (cdr lis))  ; the length of LIS.
1171;            (cond ((pred elt)
1172;                   (set-cdr! lis in)
1173;                   (values lis out))
1174;                  (else (set-cdr! lis out)
1175;                        (values in lis))))))))
1176
1177
1178;;; This implementation of PARTITION!
1179;;; - doesn't cons, and uses no stack;
1180;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are
1181;;;   usually expensive on modern machines, and can be extremely expensive on
1182;;;   modern Schemes (e.g., ones that have generational GC's).
1183;;; It just zips down contiguous runs of in and out elts in LIS doing the
1184;;; minimal number of SET-CDR!s to splice these runs together into the result
1185;;; lists.
1186
1187(define (partition! pred lis)
1188;  (check-arg procedure? pred partition!)
1189  (if (null-list? lis) (values lis lis)
1190
1191      ;; This pair of loops zips down contiguous in & out runs of the
1192      ;; list, splicing the runs together. The invariants are
1193      ;;   SCAN-IN:  (cdr in-prev)  = LIS.
1194      ;;   SCAN-OUT: (cdr out-prev) = LIS.
1195      (letrec ((scan-in (lambda (in-prev out-prev lis)
1196                          (let lp ((in-prev in-prev) (lis lis))
1197                            (if (pair? lis)
1198                                (if (pred (car lis))
1199                                    (lp lis (cdr lis))
1200                                    (begin (set-cdr! out-prev lis)
1201                                           (scan-out in-prev lis (cdr lis))))
1202                                (set-cdr! out-prev lis))))) ; Done.
1203
1204               (scan-out (lambda (in-prev out-prev lis)
1205                           (let lp ((out-prev out-prev) (lis lis))
1206                             (if (pair? lis)
1207                                 (if (pred (car lis))
1208                                     (begin (set-cdr! in-prev lis)
1209                                            (scan-in lis out-prev (cdr lis)))
1210                                     (lp lis (cdr lis)))
1211                                 (set-cdr! in-prev lis)))))) ; Done.
1212
1213        ;; Crank up the scan&splice loops.
1214        (if (pred (car lis))
1215            ;; LIS begins in-list. Search for out-list's first pair.
1216            (let lp ((prev-l lis) (l (cdr lis)))
1217              (cond ((not (pair? l)) (values lis l))
1218                    ((pred (car l)) (lp l (cdr l)))
1219                    (else (scan-out prev-l l (cdr l))
1220                          (values lis l))))     ; Done.
1221
1222            ;; LIS begins out-list. Search for in-list's first pair.
1223            (let lp ((prev-l lis) (l (cdr lis)))
1224              (cond ((not (pair? l)) (values l lis))
1225                    ((pred (car l))
1226                     (scan-in l prev-l (cdr l))
1227                     (values l lis))            ; Done.
1228                    (else (lp l (cdr l)))))))))
1229
1230
1231;;; Inline us, please.
1232(define (remove  pred l) (filter  (lambda (x) (not (pred x))) l))
1233(define (remove! pred l) (filter! (lambda (x) (not (pred x))) l))
1234
1235
1236
1237;;; Here's the taxonomy for the DELETE/ASSOC/MEMBER functions.
1238;;; (I don't actually think these are the world's most important
1239;;; functions -- the procedural FILTER/REMOVE/FIND/FIND-TAIL variants
1240;;; are far more general.)
1241;;;
1242;;; Function                    Action
1243;;; ---------------------------------------------------------------------------
1244;;; remove pred lis             Delete by general predicate
1245;;; delete x lis [=]            Delete by element comparison
1246;;;                                         
1247;;; find pred lis               Search by general predicate
1248;;; find-tail pred lis          Search by general predicate
1249;;; member x lis [=]            Search by element comparison
1250;;;
1251;;; assoc key lis [=]           Search alist by key comparison
1252;;; alist-delete key alist [=]  Alist-delete by key comparison
1253
1254(define (delete x lis . maybe-=) 
1255  (let ((= (optional maybe-= equal?)))
1256    (filter (lambda (y) (not (= x y))) lis)))
1257
1258(define (delete! x lis . maybe-=)
1259  (let ((= (optional maybe-= equal?)))
1260    (filter! (lambda (y) (not (= x y))) lis)))
1261
1262;;; Extended from R4RS to take an optional comparison argument.
1263(define (member x lis . maybe-=)
1264  (let ((= (optional maybe-= equal?)))
1265    (find-tail (lambda (y) (= x y)) lis)))
1266
1267;;; R4RS, hence we don't bother to define.
1268;;; The MEMBER and then FIND-TAIL call should definitely
1269;;; be inlined for MEMQ & MEMV.
1270;(define (memq    x lis) (member x lis eq?))
1271;(define (memv    x lis) (member x lis eqv?))
1272
1273
1274;;; right-duplicate deletion
1275;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1276;;; delete-duplicates delete-duplicates!
1277;;;
1278;;; Beware -- these are N^2 algorithms. To efficiently remove duplicates
1279;;; in long lists, sort the list to bring duplicates together, then use a
1280;;; linear-time algorithm to kill the dups. Or use an algorithm based on
1281;;; element-marking. The former gives you O(n lg n), the latter is linear.
1282
1283(define (delete-duplicates lis . maybe-=)
1284  (let ((elt= (optional maybe-= equal?)))
1285;    (check-arg procedure? elt= delete-duplicates)
1286    (let recur ((lis lis))
1287      (if (null-list? lis) lis
1288          (let* ((x (car lis))
1289                 (tail (cdr lis))
1290                 (new-tail (recur (delete x tail elt=))))
1291            (if (eq? tail new-tail) lis (cons x new-tail)))))))
1292
1293(define (delete-duplicates! lis . maybe-=)
1294  (let ((elt= (optional maybe-= equal?)))
1295;    (check-arg procedure? elt= delete-duplicates!)
1296    (let recur ((lis lis))
1297      (if (null-list? lis) lis
1298          (let* ((x (car lis))
1299                 (tail (cdr lis))
1300                 (new-tail (recur (delete! x tail elt=))))
1301            (if (eq? tail new-tail) lis (cons x new-tail)))))))
1302
1303
1304;;; alist stuff
1305;;;;;;;;;;;;;;;
1306
1307;;; Extended from R4RS to take an optional comparison argument.
1308(define (assoc x lis . maybe-=)
1309  (let ((= (optional maybe-= equal?)))
1310    (find (lambda (entry) (= x (car entry))) lis)))
1311
1312(define (alist-cons key datum alist) (cons (cons key datum) alist))
1313
1314(define (alist-copy alist)
1315  (##sys#map (lambda (elt) (cons (car elt) (cdr elt)))
1316       alist))
1317
1318(define (alist-delete key alist . maybe-=)
1319  (let ((= (optional maybe-= equal?)))
1320    (filter (lambda (elt) (not (= key (car elt)))) alist)))
1321
1322(define (alist-delete! key alist . maybe-=)
1323  (let ((= (optional maybe-= equal?)))
1324    (filter! (lambda (elt) (not (= key (car elt)))) alist)))
1325
1326
1327;;; find find-tail take-while drop-while span break any every list-index
1328;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1329
1330(define (find pred list)
1331  (cond ((find-tail pred list) => car)
1332        (else #f)))
1333
1334(define (find-tail pred list)
1335;  (check-arg procedure? pred find-tail)
1336  (let lp ((list list))
1337    (and (not (null-list? list))
1338         (if (pred (car list)) list
1339             (lp (cdr list))))))
1340
1341(define (take-while pred lis)
1342;  (check-arg procedure? pred take-while)
1343  (let recur ((lis lis))
1344    (if (null-list? lis) '()
1345        (let ((x (car lis)))
1346          (if (pred x)
1347              (cons x (recur (cdr lis)))
1348              '())))))
1349
1350(define (drop-while pred lis)
1351;  (check-arg procedure? pred drop-while)
1352  (let lp ((lis lis))
1353    (if (null-list? lis) '()
1354        (if (pred (car lis))
1355            (lp (cdr lis))
1356            lis))))
1357
1358(define (take-while! pred lis)
1359;  (check-arg procedure? pred take-while!)
1360  (if (or (null-list? lis) (not (pred (car lis)))) '()
1361      (begin (let lp ((prev lis) (rest (cdr lis)))
1362               (if (pair? rest)
1363                   (let ((x (car rest)))
1364                     (if (pred x) (lp rest (cdr rest))
1365                         (set-cdr! prev '())))))
1366             lis)))
1367
1368(define (span pred lis)
1369;  (check-arg procedure? pred span)
1370  (let recur ((lis lis))
1371    (if (null-list? lis) (values '() '())
1372        (let ((x (car lis)))
1373          (if (pred x)
1374              (receive (prefix suffix) (recur (cdr lis))
1375                (values (cons x prefix) suffix))
1376              (values '() lis))))))
1377
1378(define (span! pred lis)
1379;  (check-arg procedure? pred span!)
1380  (if (or (null-list? lis) (not (pred (car lis)))) (values '() lis)
1381      (let ((suffix (let lp ((prev lis) (rest (cdr lis)))
1382                      (if (null-list? rest) rest
1383                          (let ((x (car rest)))
1384                            (if (pred x) (lp rest (cdr rest))
1385                                (begin (set-cdr! prev '())
1386                                       rest)))))))
1387        (values lis suffix))))
1388 
1389
1390(define (break  pred lis) (span  (lambda (x) (not (pred x))) lis))
1391(define (break! pred lis) (span! (lambda (x) (not (pred x))) lis))
1392
1393(define (any pred lis1 . lists)
1394;  (check-arg procedure? pred any)
1395  (if (pair? lists)
1396
1397      ;; N-ary case
1398      (receive (heads tails) (##srfi1#cars+cdrs (cons lis1 lists))
1399        (and (pair? heads)
1400             (let lp ((heads heads) (tails tails))
1401               (receive (next-heads next-tails) (##srfi1#cars+cdrs tails)
1402                 (if (pair? next-heads)
1403                     (or (apply pred heads) (lp next-heads next-tails))
1404                     (apply pred heads)))))) ; Last PRED app is tail call.
1405
1406      ;; Fast path
1407      (and (not (null-list? lis1))
1408           (let lp ((head (car lis1)) (tail (cdr lis1)))
1409             (if (null-list? tail)
1410                 (pred head)            ; Last PRED app is tail call.
1411                 (or (pred head) (lp (car tail) (cdr tail))))))))
1412
1413
1414;(define (every pred list)              ; Simple definition.
1415;  (let lp ((list list))                ; Doesn't return the last PRED value.
1416;    (or (not (pair? list))
1417;        (and (pred (car list))
1418;             (lp (cdr list))))))
1419
1420(define (every pred lis1 . lists)
1421;  (check-arg procedure? pred every)
1422  (if (pair? lists)
1423
1424      ;; N-ary case
1425      (receive (heads tails) (##srfi1#cars+cdrs (cons lis1 lists))
1426        (or (not (pair? heads))
1427            (let lp ((heads heads) (tails tails))
1428              (receive (next-heads next-tails) (##srfi1#cars+cdrs tails)
1429                (if (pair? next-heads)
1430                    (and (apply pred heads) (lp next-heads next-tails))
1431                    (apply pred heads)))))) ; Last PRED app is tail call.
1432
1433      ;; Fast path
1434      (or (null-list? lis1)
1435          (let lp ((head (car lis1))  (tail (cdr lis1)))
1436            (if (null-list? tail)
1437                (pred head)     ; Last PRED app is tail call.
1438                (and (pred head) (lp (car tail) (cdr tail))))))))
1439
1440(define (list-index pred lis1 . lists)
1441;  (check-arg procedure? pred list-index)
1442  (if (pair? lists)
1443
1444      ;; N-ary case
1445      (let lp ((lists (cons lis1 lists)) (n 0))
1446        (receive (heads tails) (##srfi1#cars+cdrs lists)
1447          (and (pair? heads)
1448               (if (apply pred heads) n
1449                   (lp tails (fx+ n 1))))))
1450
1451      ;; Fast path
1452      (let lp ((lis lis1) (n 0))
1453        (and (not (null-list? lis))
1454             (if (pred (car lis)) n (lp (cdr lis) (fx+ n 1)))))))
1455
1456;;; Reverse
1457;;;;;;;;;;;
1458
1459;R4RS, so not defined here.
1460;(define (reverse lis) (fold cons '() lis))
1461                                     
1462;(define (reverse! lis)
1463;  (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair) '() lis))
1464
1465(define (reverse! lis)
1466  (let lp ((lis lis) (ans '()))
1467    (if (null-list? lis) ans
1468        (let ((tail (cdr lis)))
1469          (set-cdr! lis ans)
1470          (lp tail lis)))))
1471
1472;;; Lists-as-sets
1473;;;;;;;;;;;;;;;;;
1474
1475;;; This is carefully tuned code; do not modify casually.
1476;;; - It is careful to share storage when possible;
1477;;; - Side-effecting code tries not to perform redundant writes.
1478;;; - It tries to avoid linear-time scans in special cases where constant-time
1479;;;   computations can be performed.
1480;;; - It relies on similar properties from the other list-lib procs it calls.
1481;;;   For example, it uses the fact that the implementations of MEMBER and
1482;;;   FILTER in this source code share longest common tails between args
1483;;;   and results to get structure sharing in the lset procedures.
1484
1485(define (##srfi1#lset2<= = lis1 lis2) (every (lambda (x) (member x lis2 =)) lis1))
1486
1487(define (lset<= = . lists)
1488;  (check-arg procedure? = lset<=)
1489  (##sys#check-closure = 'lset<=)
1490  (or (not (pair? lists)) ; 0-ary case
1491      (let lp ((s1 (car lists)) (rest (cdr lists)))
1492        (or (not (pair? rest))
1493            (let ((s2 (car rest))  (rest (cdr rest)))
1494              (and (or (eq? s2 s1)      ; Fast path
1495                       (##srfi1#lset2<= = s1 s2)) ; Real test
1496                   (lp s2 rest)))))))
1497
1498(define (lset= = . lists)
1499;  (check-arg procedure? = lset=)
1500  (##sys#check-closure = 'lset=)
1501  (or (not (pair? lists)) ; 0-ary case
1502      (let lp ((s1 (car lists)) (rest (cdr lists)))
1503        (or (not (pair? rest))
1504            (let ((s2   (car rest))
1505                  (rest (cdr rest)))
1506              (and (or (eq? s1 s2)      ; Fast path
1507                       (and (##srfi1#lset2<= = s1 s2) (##srfi1#lset2<= = s2 s1))) ; Real test
1508                   (lp s2 rest)))))))
1509
1510
1511(define (lset-adjoin = lis . elts)
1512;  (check-arg procedure? = lset-adjoin)
1513  (##sys#check-closure = 'lset-adjoin)
1514  (fold (lambda (elt ans) (if (member elt ans =) ans (cons elt ans)))
1515        lis elts))
1516
1517
1518(define (lset-union = . lists)
1519;  (check-arg procedure? = lset-union)
1520  (##sys#check-closure = 'lset-union)
1521  (reduce (lambda (lis ans)             ; Compute ANS + LIS.
1522            (cond ((null? lis) ans)     ; Don't copy any lists
1523                  ((null? ans) lis)     ; if we don't have to.
1524                  ((eq? lis ans) ans)
1525                  (else
1526                   (fold (lambda (elt ans) (if (any (lambda (x) (= x elt)) ans)
1527                                               ans
1528                                               (cons elt ans)))
1529                         ans lis))))
1530          '() lists))
1531
1532(define (lset-union! = . lists)
1533;  (check-arg procedure? = lset-union!)
1534  (##sys#check-closure = 'lset-union!)
1535  (reduce (lambda (lis ans)             ; Splice new elts of LIS onto the front of ANS.
1536            (cond ((null? lis) ans)     ; Don't copy any lists
1537                  ((null? ans) lis)     ; if we don't have to.
1538                  ((eq? lis ans) ans)
1539                  (else
1540                   (pair-fold (lambda (pair ans)
1541                                (let ((elt (car pair)))
1542                                  (if (any (lambda (x) (= x elt)) ans)
1543                                      ans
1544                                      (begin (set-cdr! pair ans) pair))))
1545                              ans lis))))
1546          '() lists))
1547
1548
1549(define (lset-intersection = lis1 . lists)
1550;  (check-arg procedure? = lset-intersection)
1551  (##sys#check-closure = 'lset-intersection)
1552  (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.
1553    (cond ((any null-list? lists) '())          ; Short cut
1554          ((null? lists)          lis1)         ; Short cut
1555          (else (filter (lambda (x)
1556                          (every (lambda (lis) (member x lis =)) lists))
1557                        lis1)))))
1558
1559(define (lset-intersection! = lis1 . lists)
1560;  (check-arg procedure? = lset-intersection!)
1561  (##sys#check-closure = 'lset-intersection!)
1562  (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.
1563    (cond ((any null-list? lists) '())          ; Short cut
1564          ((null? lists)          lis1)         ; Short cut
1565          (else (filter! (lambda (x)
1566                           (every (lambda (lis) (member x lis =)) lists))
1567                         lis1)))))
1568
1569
1570(define (lset-difference = lis1 . lists)
1571;  (check-arg procedure? = lset-difference)
1572  (##sys#check-closure = 'lset-difference)
1573  (let ((lists (filter pair? lists)))   ; Throw out empty lists.
1574    (cond ((null? lists)     lis1)      ; Short cut
1575          ((memq lis1 lists) '())       ; Short cut
1576          (else (filter (lambda (x)
1577                          (every (lambda (lis) (not (member x lis =)))
1578                                 lists))
1579                        lis1)))))
1580
1581(define (lset-difference! = lis1 . lists)
1582;  (check-arg procedure? = lset-difference!)
1583  (##sys#check-closure = 'lset-difference!)
1584  (let ((lists (filter pair? lists)))   ; Throw out empty lists.
1585    (cond ((null? lists)     lis1)      ; Short cut
1586          ((memq lis1 lists) '())       ; Short cut
1587          (else (filter! (lambda (x)
1588                           (every (lambda (lis) (not (member x lis =)))
1589                                  lists))
1590                         lis1)))))
1591
1592
1593(define (lset-xor = . lists)
1594;  (check-arg procedure? = lset-xor)
1595  (##sys#check-closure = 'lset-xor)
1596  (reduce (lambda (b a)                 ; Compute A xor B:
1597            ;; Note that this code relies on the constant-time
1598            ;; short-cuts provided by LSET-DIFF+INTERSECTION,
1599            ;; LSET-DIFFERENCE & APPEND to provide constant-time short
1600            ;; cuts for the cases A = (), B = (), and A eq? B. It takes
1601            ;; a careful case analysis to see it, but it's carefully
1602            ;; built in.
1603
1604            ;; Compute a-b and a^b, then compute b-(a^b) and
1605            ;; cons it onto the front of a-b.
1606            (receive (a-b a-int-b)   (lset-diff+intersection = a b)
1607              (cond ((null? a-b)     (lset-difference = b a))
1608                    ((null? a-int-b) (append b a))
1609                    (else (fold (lambda (xb ans)
1610                                  (if (member xb a-int-b =) ans (cons xb ans)))
1611                                a-b
1612                                b)))))
1613          '() lists))
1614
1615
1616(define (lset-xor! = . lists)
1617;  (check-arg procedure? = lset-xor!)
1618  (##sys#check-closure = 'lset-xor!)
1619  (reduce (lambda (b a)                 ; Compute A xor B:
1620            ;; Note that this code relies on the constant-time
1621            ;; short-cuts provided by LSET-DIFF+INTERSECTION,
1622            ;; LSET-DIFFERENCE & APPEND to provide constant-time short
1623            ;; cuts for the cases A = (), B = (), and A eq? B. It takes
1624            ;; a careful case analysis to see it, but it's carefully
1625            ;; built in.
1626
1627            ;; Compute a-b and a^b, then compute b-(a^b) and
1628            ;; cons it onto the front of a-b.
1629            (receive (a-b a-int-b)   (lset-diff+intersection! = a b)
1630              (cond ((null? a-b)     (lset-difference! = b a))
1631                    ((null? a-int-b) (append! b a))
1632                    (else (pair-fold (lambda (b-pair ans)
1633                                       (if (member (car b-pair) a-int-b =) ans
1634                                           (begin (set-cdr! b-pair ans) b-pair)))
1635                                     a-b
1636                                     b)))))
1637          '() lists))
1638
1639
1640(define (lset-diff+intersection = lis1 . lists)
1641;  (check-arg procedure? = lset-diff+intersection)
1642  (##sys#check-closure = 'lset-diff+intersection)
1643  (cond ((every null-list? lists) (values lis1 '()))    ; Short cut
1644        ((memq lis1 lists)        (values '() lis1))    ; Short cut
1645        (else (partition (lambda (elt)
1646                           (not (any (lambda (lis) (member elt lis =))
1647                                     lists)))
1648                         lis1))))
1649
1650(define (lset-diff+intersection! = lis1 . lists)
1651;  (check-arg procedure? = lset-diff+intersection!)
1652  (##sys#check-closure = 'lset-diff+intersection!)
1653  (cond ((every null-list? lists) (values lis1 '()))    ; Short cut
1654        ((memq lis1 lists)        (values '() lis1))    ; Short cut
1655        (else (partition! (lambda (elt)
1656                            (not (any (lambda (lis) (member elt lis =))
1657                                      lists)))
1658                          lis1))))
1659
1660) ; module
Note: See TracBrowser for help on using the repository browser.