diff --git a/scsh/lib/list-lib.scm b/scsh/lib/list-lib.scm index c952b33..c042618 100644 --- a/scsh/lib/list-lib.scm +++ b/scsh/lib/list-lib.scm @@ -79,18 +79,34 @@ ;;; 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. 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. +;;; 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 @@ -1192,23 +1208,25 @@ ;;; 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 elt= lis) - (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 lis . maybe-=) + (let ((elt= (:optional maybe-= equal?))) + (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! elt= lis) - (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! lis maybe-=) + (let ((elt= (:optional maybe-= equal?))) + (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))))))) ;;; alist stuff @@ -1382,7 +1400,7 @@ ((null? ans) lis) ; if we don't have to. ((eq? lis ans) ans) (else - (fold (lambda (elt ans) (if (any (lambda (x) (= x elt))) + (fold (lambda (elt ans) (if (any (lambda (x) (= x elt)) ans) ans (cons elt ans))) ans lis)))) diff --git a/scsh/lib/list-pack.scm b/scsh/lib/list-pack.scm index 0c171ce..6fa562a 100644 --- a/scsh/lib/list-pack.scm +++ b/scsh/lib/list-pack.scm @@ -60,7 +60,7 @@ ;; list-copy lis (list-copy (proc (:value) :value)) - (circular-list (proc (:value &opt :value) :pair)) + (circular-list (proc (:value &rest :value) :pair)) ; ((:iota iota:) ; (proc (:number &opt :number :number) :value)) @@ -102,7 +102,7 @@ (unzip4 (proc (:value) (some-values :value :value :value :value))) (unzip5 (proc (:value) (some-values :value :value :value :value :value))) - (count (proc ((proc (:value) :boolean) :value) :exact-integer)) + (count (proc ((proc (:value &rest :value) :boolean) :value) :exact-integer)) ((fold fold-right) (proc ((proc (:value :value &rest :value) :value) diff --git a/scsh/lib/srfi-1.html b/scsh/lib/srfi-1.html index 73f9c50..8457db0 100644 --- a/scsh/lib/srfi-1.html +++ b/scsh/lib/srfi-1.html @@ -1,8 +1,7 @@ -
(cons elt1 (cons elt2 (cons ... eltn)))
list*
in Common Lisp and about
+ This function is called list*
in Common Lisp and about
half of the Schemes that provide it,
and cons*
in the other half.
@@ -783,7 +784,7 @@ an error or diverge when passed a circular list. ==== make-list ============================================================================--> -
make-list n [fill] -> list
+make-list
n [fill] -> list
- Nil-terminated lists are called "proper" lists by R5RS and Common Lisp. + Nil-terminated lists are called "proper" lists by R5RS and Common Lisp. The opposite of proper is improper.
R5RS binds this function to the variable list?
.
@@ -1011,7 +1012,8 @@ partition the entire universe of Scheme values.
(eq? x y)
=> (elt= x y)
.
Note that this implies that two lists which are eq?
- are always list=, as well.
+ are always list=, as well; implementations may exploit this
+ fact to "short-cut" the element-by-element comparisons.
(list= eq?) => #t ; Trivial cases (list= eq? '(a)) => #t @@ -1318,6 +1320,8 @@ partition the entire universe of Scheme values.- The kons function may reliably apply(append '(a b) '(c . d)) => (a b c . d) (append '() 'a) => a +(append '(x y)) => (x y) +(append) => ()append!
is the "linear-update" variant ofappend
@@ -1565,7 +1569,8 @@ partition the entire universe of Scheme values. (pair-fold kons (kons lis knil) tail)) (pair-fold kons knil'()
) = knil
set-cdr!
to the pairs it is given
+ For finite lists, the kons function may reliably apply
+ set-cdr!
to the pairs it is given
without altering the sequence of execution.
Example:
@@ -1754,10 +1759,10 @@ Otherwise, return
- At least one of the argument lists must be finite:
-(fold f (car list) (cdr li
;; List of squares: 1^2 ... 10^2
-(unfold (lambda (x) (> x 10))
- (lambda (x) (* x x))
- (lambda (x) (+ x 1))
- 1)
+(unfold-right (lambda (x) (> x 10))
+ (lambda (x) (* x x))
+ (lambda (x) (+ x 1))
+ 1)
(unfold-right null-list? car cdr lis) ; Copy a proper list.
@@ -1870,10 +1875,7 @@ Otherwise, return
(fold f (car list) (cdr li
specification to allow the arguments to be of unequal length;
it terminates when the shortest list runs out.
-(map + '(3 1 4 1) (circular-list 1 0)) => (4 1 5 1)
-
+ At least one of the argument lists must be finite.