* module/srfi/srfi-1.scm (map!): Re-use cons cells of first argument. * test-suite/tests/srfi-1.test: Test map!. ---
Hi Ludo, Thanks for your patience in getting this together. I've added some tests for map!. Let me know if you think there are more cases that should be tested. As a sidenote, it looks like regular map isn't directly tested. Maybe if I get time I'll copy these tests for it :) Thanks, Juli module/srfi/srfi-1.scm | 58 ++++++++++++++++++++++++++++++++++-- test-suite/tests/srfi-1.test | 38 +++++++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/module/srfi/srfi-1.scm b/module/srfi/srfi-1.scm index b46f7be5f..c0018b188 100644 --- a/module/srfi/srfi-1.scm +++ b/module/srfi/srfi-1.scm @@ -791,8 +791,62 @@ has just one element then that's the return value." (define (append-map! f clist1 . rest) (concatenate! (apply map f clist1 rest))) -;; OPTIMIZE-ME: Re-use cons cells of list1 -(define map! map) +(define map! + (case-lambda + ((f lst) + (check-arg procedure? f map!) + (check-arg list? lst map!) + (let map1 ((l lst)) + (if (pair? l) + (begin + (set-car! l (f (car l))) + (map1 (cdr l))) + lst))) + + ((f lst1 lst2) + (check-arg procedure? f map!) + (check-arg list? lst1 map!) + (let* ((len1 (length lst1)) + (len2 (length+ lst2)) + ;; Ensure either that all lists after the first are circular or that + ;; they are at least as long as the first + (len (and (or (not len2) + (<= len1 len2)) + len1))) + (unless len + (scm-error 'misc-error "map!" + "All argument lists must be at least as long as first: ~S" + (list (list lst1 lst2)) #f)) + (let map2 ((l1 lst1) (l2 lst2) (len len)) + (if (zero? len) + lst1 + (begin + (set-car! l1 (f (car l1) (car l2))) + (map2 (cdr l1) (cdr l2) (1- len))))))) + + ((f lst1 . rest) + (check-arg procedure? f map!) + (check-arg list? lst1 map!) + ;; Ensure either that all lists after the first are circular or that + ;; they are at least as long as the first + (let ((len (fold (lambda (ls len) + (let ((ls-len (length+ ls))) + (and len + (or (not ls-len) + (<= len ls-len)) + len))) + (length lst1) + rest))) + (unless len + (scm-error 'misc-error "map!" + "All argument lists must be at least as long as first: ~S" + (list (cons lst1 rest)) #f)) + (let mapn ((l1 lst1) (rest rest) (len len)) + (if (zero? len) + lst1 + (begin + (set-car! l1 (apply f (car l1) (map car rest))) + (mapn (cdr l1) (map cdr rest) (1- len))))))))) (define (filter-map proc list1 . rest) "Apply PROC to the elements of LIST1... and return a list of the diff --git a/test-suite/tests/srfi-1.test b/test-suite/tests/srfi-1.test index 558934df4..4263b5ac1 100644 --- a/test-suite/tests/srfi-1.test +++ b/test-suite/tests/srfi-1.test @@ -1921,6 +1921,44 @@ '(1) '(2)) good))) +;; +;; map! +;; + +(with-test-prefix "map!" + + (pass-if-exception "no args" exception:wrong-num-args + (map!)) + + (pass-if-exception "one arg" exception:wrong-num-args + (map! (lambda _ #t))) + + (pass-if-exception "non-procedure first arg" exception:wrong-type-arg + (map! 'not-a-proc '(1 2 3))) + + (pass-if-exception "non-list second arg" exception:wrong-type-arg + (map! identity '(1 2 3))) + + (pass-if "1+ (1 2 3)" + (let ((lst '(1 2 3))) + (and (eq? lst (map! 1+ lst)) + (equal? '(2 3 4) lst)))) + + (pass-if "+ (1 2 3) (3 2 1)" + (let ((l1 '(1 2 3)) + (l2 '(3 2 1))) + (and (eq? l1 (map! + l1 l2)) + (not (eq? l1 l2)) + (equal? '(4 4 4) l1)))) + + (pass-if "+ (1 1 1) (2 2 2) (3 3 3)" + (let ((l1 '(1 1 1)) + (l2 '(2 2 2)) + (l3 '(3 3 3))) + (and (eq? l1 (map! + l1 l2 l3)) + (not (eq? l1 l2 l3)) + (equal? '(6 6 6) l1))))) + ;; ;; member ;; -- 2.48.1