aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTino Calancha2017-02-27 16:32:10 +0900
committerTino Calancha2017-02-27 16:32:10 +0900
commit4daca38d5c673c5b6862e10cfade9559852cce12 (patch)
treef50e54581adcb9d69f5d927dec55ba2e13ed63bc
parent841e3e377c97142cfe76b9d91467f439198f5e39 (diff)
downloademacs-4daca38d5c673c5b6862e10cfade9559852cce12.tar.gz
emacs-4daca38d5c673c5b6862e10cfade9559852cce12.zip
Prevent for consing in cl-mapc and cl-mapl
* lisp/emacs-lisp/cl-extra.el (cl--mapcar-many): Add optional arg ACC; If non-nil, accumulate values in the result (Bug#25826). (cl-mapc): Do computations inside function instead of call cl-map. (cl-mapl): Do computations inside function instead of call cl-maplist. * lisp/emacs-lisp/cl-lib.el (mapcar): Add autoload cookie. Call cl--mapcar-many with non-nil 3rd argument. * test/lisp/emacs-lisp/cl-extra-tests.el (cl-extra-test-map) (cl-extra-test-mapc, cl-extra-test-mapcar, cl-extra-test-mapl) (cl-extra-test-maplist): New tests.
-rw-r--r--lisp/emacs-lisp/cl-extra.el38
-rw-r--r--lisp/emacs-lisp/cl-lib.el5
-rw-r--r--test/lisp/emacs-lisp/cl-extra-tests.el59
3 files changed, 88 insertions, 14 deletions
diff --git a/lisp/emacs-lisp/cl-extra.el b/lisp/emacs-lisp/cl-extra.el
index edd14b816f0..8cba9137105 100644
--- a/lisp/emacs-lisp/cl-extra.el
+++ b/lisp/emacs-lisp/cl-extra.el
@@ -89,7 +89,7 @@ strings case-insensitively."
89;;; Control structures. 89;;; Control structures.
90 90
91;;;###autoload 91;;;###autoload
92(defun cl--mapcar-many (cl-func cl-seqs) 92(defun cl--mapcar-many (cl-func cl-seqs &optional acc)
93 (if (cdr (cdr cl-seqs)) 93 (if (cdr (cdr cl-seqs))
94 (let* ((cl-res nil) 94 (let* ((cl-res nil)
95 (cl-n (apply 'min (mapcar 'length cl-seqs))) 95 (cl-n (apply 'min (mapcar 'length cl-seqs)))
@@ -106,20 +106,23 @@ strings case-insensitively."
106 (setcar cl-p1 (cdr (car cl-p1)))) 106 (setcar cl-p1 (cdr (car cl-p1))))
107 (aref (car cl-p1) cl-i))) 107 (aref (car cl-p1) cl-i)))
108 (setq cl-p1 (cdr cl-p1) cl-p2 (cdr cl-p2))) 108 (setq cl-p1 (cdr cl-p1) cl-p2 (cdr cl-p2)))
109 (push (apply cl-func cl-args) cl-res) 109 (if acc
110 (push (apply cl-func cl-args) cl-res)
111 (apply cl-func cl-args))
110 (setq cl-i (1+ cl-i))) 112 (setq cl-i (1+ cl-i)))
111 (nreverse cl-res)) 113 (and acc (nreverse cl-res)))
112 (let ((cl-res nil) 114 (let ((cl-res nil)
113 (cl-x (car cl-seqs)) 115 (cl-x (car cl-seqs))
114 (cl-y (nth 1 cl-seqs))) 116 (cl-y (nth 1 cl-seqs)))
115 (let ((cl-n (min (length cl-x) (length cl-y))) 117 (let ((cl-n (min (length cl-x) (length cl-y)))
116 (cl-i -1)) 118 (cl-i -1))
117 (while (< (setq cl-i (1+ cl-i)) cl-n) 119 (while (< (setq cl-i (1+ cl-i)) cl-n)
118 (push (funcall cl-func 120 (let ((val (funcall cl-func
119 (if (consp cl-x) (pop cl-x) (aref cl-x cl-i)) 121 (if (consp cl-x) (pop cl-x) (aref cl-x cl-i))
120 (if (consp cl-y) (pop cl-y) (aref cl-y cl-i))) 122 (if (consp cl-y) (pop cl-y) (aref cl-y cl-i)))))
121 cl-res))) 123 (when acc
122 (nreverse cl-res)))) 124 (push val cl-res)))))
125 (and acc (nreverse cl-res)))))
123 126
124;;;###autoload 127;;;###autoload
125(defun cl-map (cl-type cl-func cl-seq &rest cl-rest) 128(defun cl-map (cl-type cl-func cl-seq &rest cl-rest)
@@ -142,7 +145,7 @@ the elements themselves.
142 (while (not (memq nil cl-args)) 145 (while (not (memq nil cl-args))
143 (push (apply cl-func cl-args) cl-res) 146 (push (apply cl-func cl-args) cl-res)
144 (setq cl-p cl-args) 147 (setq cl-p cl-args)
145 (while cl-p (setcar cl-p (cdr (pop cl-p)) ))) 148 (while cl-p (setcar cl-p (cdr (pop cl-p)))))
146 (nreverse cl-res)) 149 (nreverse cl-res))
147 (let ((cl-res nil)) 150 (let ((cl-res nil))
148 (while cl-list 151 (while cl-list
@@ -155,8 +158,14 @@ the elements themselves.
155 "Like `cl-mapcar', but does not accumulate values returned by the function. 158 "Like `cl-mapcar', but does not accumulate values returned by the function.
156\n(fn FUNCTION SEQUENCE...)" 159\n(fn FUNCTION SEQUENCE...)"
157 (if cl-rest 160 (if cl-rest
158 (progn (apply 'cl-map nil cl-func cl-seq cl-rest) 161 (if (or (cdr cl-rest) (nlistp cl-seq) (nlistp (car cl-rest)))
159 cl-seq) 162 (progn
163 (cl--mapcar-many cl-func (cons cl-seq cl-rest))
164 cl-seq)
165 (let ((cl-x cl-seq) (cl-y (car cl-rest)))
166 (while (and cl-x cl-y)
167 (funcall cl-func (pop cl-x) (pop cl-y)))
168 cl-seq))
160 (mapc cl-func cl-seq))) 169 (mapc cl-func cl-seq)))
161 170
162;;;###autoload 171;;;###autoload
@@ -164,7 +173,12 @@ the elements themselves.
164 "Like `cl-maplist', but does not accumulate values returned by the function. 173 "Like `cl-maplist', but does not accumulate values returned by the function.
165\n(fn FUNCTION LIST...)" 174\n(fn FUNCTION LIST...)"
166 (if cl-rest 175 (if cl-rest
167 (apply 'cl-maplist cl-func cl-list cl-rest) 176 (let ((cl-args (cons cl-list (copy-sequence cl-rest)))
177 cl-p)
178 (while (not (memq nil cl-args))
179 (apply cl-func cl-args)
180 (setq cl-p cl-args)
181 (while cl-p (setcar cl-p (cdr (pop cl-p))))))
168 (let ((cl-p cl-list)) 182 (let ((cl-p cl-list))
169 (while cl-p (funcall cl-func cl-p) (setq cl-p (cdr cl-p))))) 183 (while cl-p (funcall cl-func cl-p) (setq cl-p (cdr cl-p)))))
170 cl-list) 184 cl-list)
diff --git a/lisp/emacs-lisp/cl-lib.el b/lisp/emacs-lisp/cl-lib.el
index 5aa8f1bf652..8c4455a3dad 100644
--- a/lisp/emacs-lisp/cl-lib.el
+++ b/lisp/emacs-lisp/cl-lib.el
@@ -347,8 +347,9 @@ Call `cl-float-limits' to set this.")
347 347
348(cl--defalias 'cl-copy-seq 'copy-sequence) 348(cl--defalias 'cl-copy-seq 'copy-sequence)
349 349
350(declare-function cl--mapcar-many "cl-extra" (cl-func cl-seqs)) 350(declare-function cl--mapcar-many "cl-extra" (cl-func cl-seqs &optional acc))
351 351
352;;;###autoload
352(defun cl-mapcar (cl-func cl-x &rest cl-rest) 353(defun cl-mapcar (cl-func cl-x &rest cl-rest)
353 "Apply FUNCTION to each element of SEQ, and make a list of the results. 354 "Apply FUNCTION to each element of SEQ, and make a list of the results.
354If there are several SEQs, FUNCTION is called with that many arguments, 355If there are several SEQs, FUNCTION is called with that many arguments,
@@ -358,7 +359,7 @@ SEQ, this is like `mapcar'. With several, it is like the Common Lisp
358\n(fn FUNCTION SEQ...)" 359\n(fn FUNCTION SEQ...)"
359 (if cl-rest 360 (if cl-rest
360 (if (or (cdr cl-rest) (nlistp cl-x) (nlistp (car cl-rest))) 361 (if (or (cdr cl-rest) (nlistp cl-x) (nlistp (car cl-rest)))
361 (cl--mapcar-many cl-func (cons cl-x cl-rest)) 362 (cl--mapcar-many cl-func (cons cl-x cl-rest) 'accumulate)
362 (let ((cl-res nil) (cl-y (car cl-rest))) 363 (let ((cl-res nil) (cl-y (car cl-rest)))
363 (while (and cl-x cl-y) 364 (while (and cl-x cl-y)
364 (push (funcall cl-func (pop cl-x) (pop cl-y)) cl-res)) 365 (push (funcall cl-func (pop cl-x) (pop cl-y)) cl-res))
diff --git a/test/lisp/emacs-lisp/cl-extra-tests.el b/test/lisp/emacs-lisp/cl-extra-tests.el
index 3e2388acc6f..5b2371e7b95 100644
--- a/test/lisp/emacs-lisp/cl-extra-tests.el
+++ b/test/lisp/emacs-lisp/cl-extra-tests.el
@@ -35,4 +35,63 @@
35 (should (eq (cl-getf plist 'y :none) nil)) 35 (should (eq (cl-getf plist 'y :none) nil))
36 (should (eq (cl-getf plist 'z :none) :none)))) 36 (should (eq (cl-getf plist 'z :none) :none))))
37 37
38(ert-deftest cl-extra-test-mapc ()
39 (let ((lst '(a b c))
40 (lst2 '(d e f))
41 (lst3 '(1 2 3))
42 (fn1 (lambda (_x) nil))
43 (fn2 (lambda (_x _y) nil))
44 (fn3 (lambda (_x _y _z) nil)))
45 (should (equal lst (cl-mapc fn1 lst)))
46 (should (equal lst (cl-mapc fn2 lst lst2)))
47 (should (equal lst (cl-mapc fn3 lst lst2 lst3)))))
48
49(ert-deftest cl-extra-test-mapl ()
50 (let ((lst '(a b c))
51 (lst2 '(d e f))
52 (lst3 '(1 2 3))
53 (fn1 (lambda (x) (should (consp x))))
54 (fn2 (lambda (x y) (should (and (consp x) (consp y)))))
55 (fn3 (lambda (x y z) (should (and (consp x) (consp y) (consp z))))))
56 (should (equal lst (cl-mapl fn1 lst)))
57 (should (equal lst (cl-mapl fn2 lst lst2)))
58 (should (equal lst (cl-mapl fn3 lst lst2 lst3)))))
59
60(ert-deftest cl-extra-test-mapcar ()
61 (let ((lst '(a b c))
62 (lst2 '(d e f))
63 (lst3 '(1 2 3))
64 (fn1 (lambda (x) x))
65 (fn2 (lambda (_x y) y))
66 (fn3 (lambda (_x _y z) z)))
67 (should (equal lst (cl-mapcar fn1 lst)))
68 (should (equal lst2 (cl-mapcar fn2 lst lst2)))
69 (should (equal lst3 (cl-mapcar fn3 lst lst2 lst3)))))
70
71(ert-deftest cl-extra-test-map ()
72 (let ((lst '(a b c))
73 (lst2 '(d e f))
74 (lst3 '(1 2 3))
75 (fn1 (lambda (x) x))
76 (fn2 (lambda (_x y) y))
77 (fn3 (lambda (x _y _z) (string-to-char (format "%S" x)))))
78 (should (equal lst (cl-map 'list fn1 lst)))
79 (should (equal (vconcat lst2) (cl-map 'vector fn2 lst lst2)))
80 (should (equal (mapconcat (lambda (x) (format "%S" x)) lst "")
81 (cl-map 'string fn3 lst lst2 lst3)))))
82
83(ert-deftest cl-extra-test-maplist ()
84 (let ((lst '(a b c))
85 (lst2 '(d e f))
86 (lst3 '(1 2 3))
87 (fn1 (lambda (x) (should (consp x)) x))
88 (fn2 (lambda (x y) (should (and (consp x) (consp y))) y))
89 (fn3 (lambda (x y z) (should (and (consp x) (consp y) (consp z))) z)))
90 (should (equal (list lst (cdr lst) (cddr lst))
91 (cl-maplist fn1 lst)))
92 (should (equal (list lst2 (cdr lst2) (cddr lst2))
93 (cl-maplist fn2 lst lst2)))
94 (should (equal (list lst3 (cdr lst3) (cddr lst3))
95 (cl-maplist fn3 lst lst2 lst3)))))
96
38;;; cl-extra-tests.el ends here 97;;; cl-extra-tests.el ends here