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.
This commit is contained in:
parent
841e3e377c
commit
4daca38d5c
3 changed files with 88 additions and 14 deletions
|
@ -89,7 +89,7 @@ strings case-insensitively."
|
|||
;;; Control structures.
|
||||
|
||||
;;;###autoload
|
||||
(defun cl--mapcar-many (cl-func cl-seqs)
|
||||
(defun cl--mapcar-many (cl-func cl-seqs &optional acc)
|
||||
(if (cdr (cdr cl-seqs))
|
||||
(let* ((cl-res nil)
|
||||
(cl-n (apply 'min (mapcar 'length cl-seqs)))
|
||||
|
@ -106,20 +106,23 @@ strings case-insensitively."
|
|||
(setcar cl-p1 (cdr (car cl-p1))))
|
||||
(aref (car cl-p1) cl-i)))
|
||||
(setq cl-p1 (cdr cl-p1) cl-p2 (cdr cl-p2)))
|
||||
(push (apply cl-func cl-args) cl-res)
|
||||
(if acc
|
||||
(push (apply cl-func cl-args) cl-res)
|
||||
(apply cl-func cl-args))
|
||||
(setq cl-i (1+ cl-i)))
|
||||
(nreverse cl-res))
|
||||
(and acc (nreverse cl-res)))
|
||||
(let ((cl-res nil)
|
||||
(cl-x (car cl-seqs))
|
||||
(cl-y (nth 1 cl-seqs)))
|
||||
(let ((cl-n (min (length cl-x) (length cl-y)))
|
||||
(cl-i -1))
|
||||
(while (< (setq cl-i (1+ cl-i)) cl-n)
|
||||
(push (funcall cl-func
|
||||
(if (consp cl-x) (pop cl-x) (aref cl-x cl-i))
|
||||
(if (consp cl-y) (pop cl-y) (aref cl-y cl-i)))
|
||||
cl-res)))
|
||||
(nreverse cl-res))))
|
||||
(let ((val (funcall cl-func
|
||||
(if (consp cl-x) (pop cl-x) (aref cl-x cl-i))
|
||||
(if (consp cl-y) (pop cl-y) (aref cl-y cl-i)))))
|
||||
(when acc
|
||||
(push val cl-res)))))
|
||||
(and acc (nreverse cl-res)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun cl-map (cl-type cl-func cl-seq &rest cl-rest)
|
||||
|
@ -142,7 +145,7 @@ the elements themselves.
|
|||
(while (not (memq nil cl-args))
|
||||
(push (apply cl-func cl-args) cl-res)
|
||||
(setq cl-p cl-args)
|
||||
(while cl-p (setcar cl-p (cdr (pop cl-p)) )))
|
||||
(while cl-p (setcar cl-p (cdr (pop cl-p)))))
|
||||
(nreverse cl-res))
|
||||
(let ((cl-res nil))
|
||||
(while cl-list
|
||||
|
@ -155,8 +158,14 @@ the elements themselves.
|
|||
"Like `cl-mapcar', but does not accumulate values returned by the function.
|
||||
\n(fn FUNCTION SEQUENCE...)"
|
||||
(if cl-rest
|
||||
(progn (apply 'cl-map nil cl-func cl-seq cl-rest)
|
||||
cl-seq)
|
||||
(if (or (cdr cl-rest) (nlistp cl-seq) (nlistp (car cl-rest)))
|
||||
(progn
|
||||
(cl--mapcar-many cl-func (cons cl-seq cl-rest))
|
||||
cl-seq)
|
||||
(let ((cl-x cl-seq) (cl-y (car cl-rest)))
|
||||
(while (and cl-x cl-y)
|
||||
(funcall cl-func (pop cl-x) (pop cl-y)))
|
||||
cl-seq))
|
||||
(mapc cl-func cl-seq)))
|
||||
|
||||
;;;###autoload
|
||||
|
@ -164,7 +173,12 @@ the elements themselves.
|
|||
"Like `cl-maplist', but does not accumulate values returned by the function.
|
||||
\n(fn FUNCTION LIST...)"
|
||||
(if cl-rest
|
||||
(apply 'cl-maplist cl-func cl-list cl-rest)
|
||||
(let ((cl-args (cons cl-list (copy-sequence cl-rest)))
|
||||
cl-p)
|
||||
(while (not (memq nil cl-args))
|
||||
(apply cl-func cl-args)
|
||||
(setq cl-p cl-args)
|
||||
(while cl-p (setcar cl-p (cdr (pop cl-p))))))
|
||||
(let ((cl-p cl-list))
|
||||
(while cl-p (funcall cl-func cl-p) (setq cl-p (cdr cl-p)))))
|
||||
cl-list)
|
||||
|
|
|
@ -347,8 +347,9 @@ Call `cl-float-limits' to set this.")
|
|||
|
||||
(cl--defalias 'cl-copy-seq 'copy-sequence)
|
||||
|
||||
(declare-function cl--mapcar-many "cl-extra" (cl-func cl-seqs))
|
||||
(declare-function cl--mapcar-many "cl-extra" (cl-func cl-seqs &optional acc))
|
||||
|
||||
;;;###autoload
|
||||
(defun cl-mapcar (cl-func cl-x &rest cl-rest)
|
||||
"Apply FUNCTION to each element of SEQ, and make a list of the results.
|
||||
If 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
|
|||
\n(fn FUNCTION SEQ...)"
|
||||
(if cl-rest
|
||||
(if (or (cdr cl-rest) (nlistp cl-x) (nlistp (car cl-rest)))
|
||||
(cl--mapcar-many cl-func (cons cl-x cl-rest))
|
||||
(cl--mapcar-many cl-func (cons cl-x cl-rest) 'accumulate)
|
||||
(let ((cl-res nil) (cl-y (car cl-rest)))
|
||||
(while (and cl-x cl-y)
|
||||
(push (funcall cl-func (pop cl-x) (pop cl-y)) cl-res))
|
||||
|
|
|
@ -35,4 +35,63 @@
|
|||
(should (eq (cl-getf plist 'y :none) nil))
|
||||
(should (eq (cl-getf plist 'z :none) :none))))
|
||||
|
||||
(ert-deftest cl-extra-test-mapc ()
|
||||
(let ((lst '(a b c))
|
||||
(lst2 '(d e f))
|
||||
(lst3 '(1 2 3))
|
||||
(fn1 (lambda (_x) nil))
|
||||
(fn2 (lambda (_x _y) nil))
|
||||
(fn3 (lambda (_x _y _z) nil)))
|
||||
(should (equal lst (cl-mapc fn1 lst)))
|
||||
(should (equal lst (cl-mapc fn2 lst lst2)))
|
||||
(should (equal lst (cl-mapc fn3 lst lst2 lst3)))))
|
||||
|
||||
(ert-deftest cl-extra-test-mapl ()
|
||||
(let ((lst '(a b c))
|
||||
(lst2 '(d e f))
|
||||
(lst3 '(1 2 3))
|
||||
(fn1 (lambda (x) (should (consp x))))
|
||||
(fn2 (lambda (x y) (should (and (consp x) (consp y)))))
|
||||
(fn3 (lambda (x y z) (should (and (consp x) (consp y) (consp z))))))
|
||||
(should (equal lst (cl-mapl fn1 lst)))
|
||||
(should (equal lst (cl-mapl fn2 lst lst2)))
|
||||
(should (equal lst (cl-mapl fn3 lst lst2 lst3)))))
|
||||
|
||||
(ert-deftest cl-extra-test-mapcar ()
|
||||
(let ((lst '(a b c))
|
||||
(lst2 '(d e f))
|
||||
(lst3 '(1 2 3))
|
||||
(fn1 (lambda (x) x))
|
||||
(fn2 (lambda (_x y) y))
|
||||
(fn3 (lambda (_x _y z) z)))
|
||||
(should (equal lst (cl-mapcar fn1 lst)))
|
||||
(should (equal lst2 (cl-mapcar fn2 lst lst2)))
|
||||
(should (equal lst3 (cl-mapcar fn3 lst lst2 lst3)))))
|
||||
|
||||
(ert-deftest cl-extra-test-map ()
|
||||
(let ((lst '(a b c))
|
||||
(lst2 '(d e f))
|
||||
(lst3 '(1 2 3))
|
||||
(fn1 (lambda (x) x))
|
||||
(fn2 (lambda (_x y) y))
|
||||
(fn3 (lambda (x _y _z) (string-to-char (format "%S" x)))))
|
||||
(should (equal lst (cl-map 'list fn1 lst)))
|
||||
(should (equal (vconcat lst2) (cl-map 'vector fn2 lst lst2)))
|
||||
(should (equal (mapconcat (lambda (x) (format "%S" x)) lst "")
|
||||
(cl-map 'string fn3 lst lst2 lst3)))))
|
||||
|
||||
(ert-deftest cl-extra-test-maplist ()
|
||||
(let ((lst '(a b c))
|
||||
(lst2 '(d e f))
|
||||
(lst3 '(1 2 3))
|
||||
(fn1 (lambda (x) (should (consp x)) x))
|
||||
(fn2 (lambda (x y) (should (and (consp x) (consp y))) y))
|
||||
(fn3 (lambda (x y z) (should (and (consp x) (consp y) (consp z))) z)))
|
||||
(should (equal (list lst (cdr lst) (cddr lst))
|
||||
(cl-maplist fn1 lst)))
|
||||
(should (equal (list lst2 (cdr lst2) (cddr lst2))
|
||||
(cl-maplist fn2 lst lst2)))
|
||||
(should (equal (list lst3 (cdr lst3) (cddr lst3))
|
||||
(cl-maplist fn3 lst lst2 lst3)))))
|
||||
|
||||
;;; cl-extra-tests.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue