]> code.delx.au - gnu-emacs/commitdiff
An efficient built-in mapcan
authorMario Lang <mlang@delysid.org>
Sat, 9 Jul 2016 23:18:47 +0000 (01:18 +0200)
committerMario Lang <mlang@delysid.org>
Sat, 9 Jul 2016 23:18:47 +0000 (01:18 +0200)
A built-in version of `mapcan' avoids consing up (and GC'ing) the
intermediate list.

* src/fns.c (Fmapcan): New built-in.
(syms_of_fns): Define.

* lisp/emacs-lisp/cl.el (mapcan): Remove defalias.

* lisp/emacs-lisp/cl-extra.el (cl-mapcan): Use built-in `mapcan'
if only one sequence is provided.

* lisp/progmodes/hideif.el (hif-delimit):
* lisp/dired-aux.el (dired-do-find-regexp):
* lisp/woman.el (woman-parse-colon-path): Use `mapcan' instead of
`cl-mapcan'.

* lisp/woman.el (eval-when-compile): Require 'cl-lib only when
compiling.

* lisp/mouse.el (mouse-buffer-menu-map):
* lisp/net/pop3.el (pop3-uidl-dele):
* lisp/progmodes/gud.el (gud-jdb-build-source-files-list):
* lisp/cedet/semantic/db-find.el (semanticdb-fast-strip-find-results):
* lisp/cedet/semantic/symref/grep.el (semantic-symref-derive-find-filepatterns):
* lisp/gnus/nnmail.el (nnmail-split-it):
* lisp/gnus/gnus-sum.el (gnus-articles-in-thread):
* lisp/gnus/gnus-registry.el (gnus-registry-sort-addresses):
* lisp/gnus/gnus-util.el (gnus-mapcar): Use `mapcan'.

16 files changed:
etc/NEWS
lisp/cedet/semantic/db-find.el
lisp/cedet/semantic/symref/grep.el
lisp/dired-aux.el
lisp/emacs-lisp/cl-extra.el
lisp/emacs-lisp/cl.el
lisp/gnus/gnus-registry.el
lisp/gnus/gnus-sum.el
lisp/gnus/gnus-util.el
lisp/gnus/nnmail.el
lisp/mouse.el
lisp/net/pop3.el
lisp/progmodes/gud.el
lisp/progmodes/hideif.el
lisp/woman.el
src/fns.c

index 5472dd84b7777cef72a9d441eab802d2d7591adb..6aef73a3c963c3d9f3b94c21d2ad47a552823232 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -557,6 +557,9 @@ ABBR is a time zone abbreviation.  The affected functions are
 *** New basic face 'fixed-pitch-serif', for a fixed-width font with serifs.
 The Info-quoted and tex-verbatim faces now default to inheriting from it.
 
+** New built-in function `mapcan' which avoids unnecessary consing (and garbage
+   collection).
+
 \f
 * Changes in Emacs 25.2 on Non-Free Operating Systems
 
index d6635a9dcef9e3f4915fcb7ac160788650e282fa..cd951804db73772dcbd53dfc9f7fc16decc64c50 100644 (file)
@@ -902,7 +902,7 @@ instead."
 This makes it appear more like the results of a `semantic-find-' call.
 This is like `semanticdb-strip-find-results', except the input list RESULTS
 will be changed."
-  (apply #'nconc (mapcar #'cdr results)))
+  (mapcan #'cdr results))
 
 (defun semanticdb-find-results-p (resultp)
   "Non-nil if RESULTP is in the form of a semanticdb search result.
index 36e97da818df9fab00d4ad0733f6e706589211bd..b232e0fb619a0f787ec5a9f288dfb330fa52b330 100644 (file)
@@ -81,7 +81,7 @@ Optional argument MODE specifies the `major-mode' to test."
         (if (null (cdr args))
             args
           `("(" ,@args
-            ,@(apply #'nconc (mapcar (lambda (s) `("-o" "-name" ,s)) pat))
+            ,@(mapcan (lambda (s) `("-o" "-name" ,s)) pat)
             ")"))))))
 
 (defvar grepflags)
index 1a4efdfd9fb3ed4c6dca432182c1444d5831c8aa..4732d9ce85cb34b76d8d0d49916a0a34125aa1cd 100644 (file)
@@ -2762,7 +2762,7 @@ REGEXP should use constructs supported by your local `grep' command."
                           (lambda (s) (concat s "/"))
                           grep-find-ignored-directories)
                          grep-find-ignored-files))
-         (xrefs (cl-mapcan
+         (xrefs (mapcan
                  (lambda (file)
                    (xref-collect-matches regexp "*" file
                                          (and (file-directory-p file)
index 8bf0675f54b6453ce8eb8692a3590ffd21fbf341..0033a94fb5c7b4ddfb88de1b4d9c5c7432e562ae 100644 (file)
@@ -173,7 +173,9 @@ the elements themselves.
 (defun cl-mapcan (cl-func cl-seq &rest cl-rest)
   "Like `cl-mapcar', but nconc's together the values returned by the function.
 \n(fn FUNCTION SEQUENCE...)"
-  (apply 'nconc (apply 'cl-mapcar cl-func cl-seq cl-rest)))
+  (if cl-rest
+      (apply 'nconc (apply 'cl-mapcar cl-func cl-seq cl-rest))
+    (mapcan cl-func cl-seq)))
 
 ;;;###autoload
 (defun cl-mapcon (cl-func cl-list &rest cl-rest)
index e48376bbabd8c2a710f638222b8a2e0d4821f5f5..fac600e4e13af58dca2d40615ceb43738e5d11e9 100644 (file)
                every
                some
                mapcon
-               mapcan
                mapl
                maplist
                map
index c636c7eb32bf9ad658779aeb6feb8f78354c6af6..37d5b5b91adbd49bfdb61b85b15b7769e0189b22 100644 (file)
@@ -826,8 +826,7 @@ Addresses without a name will say \"noname\"."
 
 (defun gnus-registry-sort-addresses (&rest addresses)
   "Return a normalized and sorted list of ADDRESSES."
-  (sort (apply 'nconc (mapcar 'gnus-registry-extract-addresses addresses))
-        'string-lessp))
+  (sort (mapcan 'gnus-registry-extract-addresses addresses) 'string-lessp))
 
 (defun gnus-registry-simplify-subject (subject)
   (if (stringp subject)
index a81a4e24c41f852c050b50a89dccea3d36172b09..910c796915aef5c42e7e808639109e12274215fe 100644 (file)
@@ -4749,7 +4749,7 @@ If LINE, insert the rebuilt thread starting on line LINE."
 (defun gnus-articles-in-thread (thread)
   "Return the list of articles in THREAD."
   (cons (mail-header-number (car thread))
-       (apply 'nconc (mapcar 'gnus-articles-in-thread (cdr thread)))))
+       (mapcan 'gnus-articles-in-thread (cdr thread))))
 
 (defun gnus-remove-thread (id &optional dont-remove)
   "Remove the thread that has ID in it."
index 906ea6037708cf6f5f51c8c38c555555edf91c9c..b6ef4334e7e7acc3d845d2f85ba367f58f50b947 100644 (file)
@@ -1599,7 +1599,7 @@ sequence, this is like `mapcar'.  With several, it is like the Common Lisp
                                                           heads))
                                        nil))
             (setq ,result-tail (cdr ,result-tail)
-                  ,@(apply 'nconc (mapcar (lambda (h) (list h (list 'cdr h))) heads))))
+                  ,@(mapcan (lambda (h) (list h (list 'cdr h))) heads)))
           (cdr ,result)))
     `(mapcar ,function ,seq1)))
 
index 65a92e904e367dce8318ecd7a8fe4c88049a8409..5495510d94a0201434809ca0ef26733e6b6879d9 100644 (file)
@@ -1372,7 +1372,7 @@ See the documentation for the variable `nnmail-split-fancy' for details."
 
      ;; Builtin & operation.
      ((eq (car split) '&)
-      (apply 'nconc (mapcar 'nnmail-split-it (cdr split))))
+      (mapcan 'nnmail-split-it (cdr split)))
 
      ;; Builtin | operation.
      ((eq (car split) '|)
index 135e1f5d71fab933e15066067bbb0d44bae5486e..44462385b6c86ed0c0d1ad98e943913e75963cf3 100644 (file)
@@ -1638,8 +1638,8 @@ and selects that window."
              (let ((others-list
                     (mouse-buffer-menu-alist
                      ;; we don't need split-by-major-mode any more,
-                     ;; so we can ditch it with nconc.
-                     (apply 'nconc (mapcar 'cddr split-by-major-mode)))))
+                     ;; so we can ditch it with nconc (mapcan).
+                     (mapcan 'cddr split-by-major-mode))))
                (and others-list
                     (setq subdivided-menus
                           (cons (cons "Others" others-list)
index d09c1d00fad6c37f5d719ac5c25977e2e934f8e7..3964288fd2320f9cf531c5101173ffcd42dee6c7 100644 (file)
@@ -402,8 +402,7 @@ Return non-nil if it is necessary to update the local UIDL file."
               (push uidl new))
             (decf i)))
          (pop3-uidl
-          (setq new (apply 'nconc (mapcar (lambda (elt) (list elt ctime))
-                                          pop3-uidl)))))
+          (setq new (mapcan (lambda (elt) (list elt ctime)) pop3-uidl))))
     (when new (setq mod t))
     ;; List expirable messages and delete them from the data to be saved.
     (setq ctime (when (numberp pop3-leave-mail-on-server)
index 9bf739463edce7cd8035e19fca1891f9cab8ff41..ceb57b7156dae6f0f3fb9bf96ccfac196b53764b 100644 (file)
@@ -1947,10 +1947,10 @@ the source code display in sync with the debugging session.")
 PATH gives the directories in which to search for files with
 extension EXTN.  Normally EXTN is given as the regular expression
  \"\\.java$\" ."
-  (apply 'nconc (mapcar (lambda (d)
-                         (when (file-directory-p d)
-                           (directory-files d t extn nil)))
-                       path)))
+  (mapcan (lambda (d)
+            (when (file-directory-p d)
+              (directory-files d t extn nil)))
+          path))
 
 ;; Move point past whitespace.
 (defun gud-jdb-skip-whitespace ()
index 6b5f51a3fbd6d4ea62f75d6cbd6bbcb4e715ac70..9fbb7d6ad32448343b15f2604767899b16907a05 100644 (file)
@@ -1114,8 +1114,8 @@ preprocessing token"
       result)))
 
 (defun hif-delimit (lis atom)
-  (nconc (cl-mapcan (lambda (l) (list l atom))
-                    (butlast lis))
+  (nconc (mapcan (lambda (l) (list l atom))
+                 (butlast lis))
          (last lis)))
 
 ;; Perform token replacement:
index 8189f08b09777313da4f9dd00ba43ecb6b25f41d..b3162074c4ee144d1ded60e1a931221872c06481 100644 (file)
               (substring arg 0 (match-end 1))
             arg))))
 
-(require 'cl-lib)
-
 (eval-when-compile                     ; to avoid compiler warnings
+  (require 'cl-lib)
   (require 'dired)
   (require 'apropos))
 
@@ -434,7 +433,7 @@ As a special case, if PATHS is nil then replace it by calling
             (mapcar 'woman-Cyg-to-Win (woman-parse-man.conf)))
            ((string-match-p ";" paths)
             ;; Assume DOS-style path-list...
-            (cl-mapcan                 ; splice list into list
+            (mapcan                    ; splice list into list
              (lambda (x)
                (if x
                    (list x)
@@ -445,14 +444,14 @@ As a special case, if PATHS is nil then replace it by calling
             (list paths))
            (t
             ;; Assume UNIX/Cygwin-style path-list...
-            (cl-mapcan                 ; splice list into list
+            (mapcan                    ; splice list into list
              (lambda (x)
                (mapcar 'woman-Cyg-to-Win
                        (if x (list x) (woman-parse-man.conf))))
              (let ((path-separator ":"))
                (parse-colon-path paths)))))
     ;; Assume host-default-style path-list...
-    (cl-mapcan                         ; splice list into list
+    (mapcan                            ; splice list into list
      (lambda (x) (if x (list x) (woman-parse-man.conf)))
      (parse-colon-path (or paths "")))))
 
index dbee33aa9f8df291981507485f9d059c49e0659f..270dfb41c172a0740f75916c2dc9450113d30155 100644 (file)
--- a/src/fns.c
+++ b/src/fns.c
@@ -2654,6 +2654,30 @@ SEQUENCE may be a list, a vector, a bool-vector, or a string.  */)
 
   return sequence;
 }
+
+DEFUN ("mapcan", Fmapcan, Smapcan, 2, 2, 0,
+       doc: /* Apply FUNCTION to each element of SEQUENCE, and concatenate
+the results by altering them (using `nconc').
+SEQUENCE may be a list, a vector, a bool-vector, or a string. */)
+     (Lisp_Object function, Lisp_Object sequence)
+{
+  register EMACS_INT leni;
+  register Lisp_Object *args;
+  Lisp_Object ret;
+  USE_SAFE_ALLOCA;
+
+  if (CHAR_TABLE_P (sequence))
+    wrong_type_argument (Qlistp, sequence);
+
+  leni = XFASTINT (Flength (sequence));
+  SAFE_ALLOCA_LISP (args, leni);
+  mapcar1 (leni, args, function, sequence);
+  ret = Fnconc (leni, args);
+
+  SAFE_FREE ();
+
+  return ret;
+}
 \f
 /* This is how C code calls `yes-or-no-p' and allows the user
    to redefine it.  */
@@ -5203,6 +5227,7 @@ this variable.  */);
   defsubr (&Snconc);
   defsubr (&Smapcar);
   defsubr (&Smapc);
+  defsubr (&Smapcan);
   defsubr (&Smapconcat);
   defsubr (&Syes_or_no_p);
   defsubr (&Sload_average);