From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: akater Newsgroups: gmane.emacs.devel Subject: Re: [PATCH] Add cl-map-into, revision 3 Date: Sat, 09 Oct 2021 02:46:17 +0000 Message-ID: <87a6jidifa.fsf@gmail.com> References: <87h7eag90n.fsf@gmail.com> <87wnmprajk.fsf@gmail.com> <83o8811ex5.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="33743"; mail-complaints-to="usenet@ciao.gmane.io" Cc: monnier@iro.umontreal.ca, emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sat Oct 09 04:59:02 2021 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mZ2Zh-0008Wl-R1 for ged-emacs-devel@m.gmane-mx.org; Sat, 09 Oct 2021 04:59:02 +0200 Original-Received: from localhost ([::1]:47840 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mZ2Zg-0006XS-JR for ged-emacs-devel@m.gmane-mx.org; Fri, 08 Oct 2021 22:59:00 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:46314) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mZ2Yk-0005r0-Rm for emacs-devel@gnu.org; Fri, 08 Oct 2021 22:58:02 -0400 Original-Received: from mail-ed1-x529.google.com ([2a00:1450:4864:20::529]:38598) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1mZ2Yi-00078V-L0; Fri, 08 Oct 2021 22:58:02 -0400 Original-Received: by mail-ed1-x529.google.com with SMTP id d9so19225822edh.5; Fri, 08 Oct 2021 19:57:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:in-reply-to:references:date:message-id :mime-version; bh=m5LIUVYG2o+qlpnsVHLtWVtf2tJeF6p7xxQGQeNws1s=; b=juBfoda8yZWJwSBllq2wmcNBJ6byQ7TvqHr5yV4+0sul+sQ6RZ3qwiuiu+h2GkN6Bo HVx764JFm/gFdstKSFYX6/3j7YSeqYX0OtLKNBiEp4xltuGaxOz0fMMD777X6cGx3ghx l+86cjkYjXhr9A5BLkhHnHCMLARK0NXZNmJkZLqX7zXxej/4LoXruL7GQvehlbdJEVGK 6YUrBLQLnmkJLyAFQKVcNlDAJcovqw5BJaoUp2K4oF/pricr8pxe9uSFZVIWqdscUvXD 0fcbS1l7Z5x1cC6sWjD0gb7cNT5rD/o9x8XF8secazh+TkLzt1JOpOuO/U55BWRVbFQk rH/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:in-reply-to:references:date :message-id:mime-version; bh=m5LIUVYG2o+qlpnsVHLtWVtf2tJeF6p7xxQGQeNws1s=; b=A2YqLxrQbdek+KdPdqFXnLG6TVNUtA7jloNSOMjaUzvGxxAu/uzW/FnZ+9OJNR1jWa CjHfW1++VHwzA30bfqMcRnhDoMyUDAyeVKBQUy4TLtMG1LcEcdtOnJa/VQEi0U3Q4zZp xQZpimfAhuNRybLtSG2wQyeEncS/lR9UA8RoIieUeZ2JSAnEUwlj8vjqbxDG50SHaLHt MyjiJtDFyUvTssMK8FvtDmIID3Grg3GisPxr4FtWCS/lLDb3L870+vFZl5N/0GFhrWOg OnVSbqDHdavEfSnp1S1A+Y3hy2sfL/NxTiYXhn7hoYCW87gCo+OmeAbjzq3BaSyeGz/N nDWA== X-Gm-Message-State: AOAM533tJXW6ERjMgs1aCp8xtpSN6t36ugmgcZ7yfPB8HHTDmRaNKF/R 3FsouPGrvibqtUgg1L/lZeIhQkrnsk8= X-Google-Smtp-Source: ABdhPJwVTeKNXCNt6nYb/YlNBKj6tT4ZJCnyo7FSLuNFf4Npi087RF8Pjr9bp8zlA8mF3c+iaDk3bg== X-Received: by 2002:aa7:d88e:: with SMTP id u14mr20737798edq.391.1633748277563; Fri, 08 Oct 2021 19:57:57 -0700 (PDT) Original-Received: from localhost ([45.153.160.134]) by smtp.googlemail.com with ESMTPSA id w2sm450683edj.44.2021.10.08.19.57.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 08 Oct 2021 19:57:56 -0700 (PDT) In-Reply-To: <83o8811ex5.fsf@gnu.org> Received-SPF: pass client-ip=2a00:1450:4864:20::529; envelope-from=nuclearspace@gmail.com; helo=mail-ed1-x529.google.com X-Spam_score_int: -7 X-Spam_score: -0.8 X-Spam_bar: / X-Spam_report: (-0.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_BL_SPAMCOP_NET=1.347, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:276584 Archived-At: --=-=-= Content-Type: multipart/signed; boundary="==-=-="; micalg=pgp-sha512; protocol="application/pgp-signature" --==-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Eli Zaretskii writes: >> +(cl-defmacro cl--do-seq-type-signature ((type-var signature &optional r= esult) >> + &body body) >> + "With TYPE-VAR bound to sequence type, evaluate BODY forms. Return R= ESULT. > > The first line of a doc string should be a single sentence, not longer > than 79 characters. > >> +(defun cl--make-map-into-mapper (signature &optional do-not-compile) >> + "Return mapper for `cl-map-into' specialized on arglists of type >> +encoded by SIGNATURE. > > Same here. This one is not public interface though. But I fixed this one nevertheless. >> +(defun cl-map-into (target function &rest sequences) >> + "Common Lisp's map-into. > > The first line of a doc string of a public interface should describe > the arguments, at least the mandatory ones. Done. Changes: =2D NEWS (29) entry =2D entry in cl.texi =2D supported are list, vector, bool-vector, string =2D some more tests =2D make-mapper code is simplified =2D =E2=80=9Ctarget=E2=80=9D renamed to =E2=80=9Cresult-sequence=E2=80=9D b= ecause that's the way it is in cl spec =2D clean docstrings Three points remain. =2D Regarding =E2=80=9Cdo-not-compile=E2=80=9D argument in make-mapper. It= would be better to have =E2=80=9Ccompile=E2=80=9D argument instead, with 3 possibl= e values: nil, byte-compile, native-compile. Native-compile seems to work right now but I'm just getting acquainted with the feature, it's not going smooth, and I'm not sure whether native-compile can be used by default in cl-map-into. If cl-map-into won't make it into Emacs 28, I suggest using native-compile right away, for ease of experimentation since nothing depends on cl-map-into right now. =2D I prefer providing examples for functions, including =E2=80=9Cinternal= =E2=80=9D ones; most of the time examples come naturally during development so it's better to use them than to let them go to waste. Usually examples are nowhere to submit; I thus often leave them in docstrings, especially when it comes to =E2=80=9Cinternal=E2=80=9D functions. This is the case = with this patch. While people do this sometimes, and there's even a Common Lisp library that addresses this technique in some way, I'm not sure if this is appropriate style. =2D I left a comment block in the beginning. Since the existing mapper in cl-extra is buggy, I'd rather have at least some of the comments remain. It will get into sight of more people this way than a mere bug in the tracker, and implmenting new similar dispatchers seems straightforward. I'll report the bug in coming days unless the bug is already there. Also commented are (possible) type declarations. I think they convey something useful as well. --==-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQJLBAEBCgA1FiEEgu5SJRdnQOF34djNsr6xYbHsf0QFAmFhAn8XHG51Y2xlYXJz cGFjZUBnbWFpbC5jb20ACgkQsr6xYbHsf0QFcxAAuQ4Az5Zpe2Bmy2KcOJ7XfaaW tAl04iLfWQGXw6ClBPv9R+SorYQnz8ele49gLdkKbRpgWV8WXlM8Md+f19mo6ALR 2aKYvj1zyNGY5RQzJBoejmDQC91e/ULcqtk8gqQph7YHmtykGu7+EmhRwTpobBNH EYczyX27+C/lVMD5IJhUpuGX9EfRxzuHTAs+NF8H0zNQABS8DV+UaYTGe4GOVvaZ N4/8BtOb4ZGaQswSN/EvThwOvSeROOF/16pQrJNFxMo816dBEjYoQvXcctjQnXEB 0K17OhUinqgSkpGoSaZseIpEJilFvvA1ItCYPqGQXPnOR2KSgMGxYPCtl36XNuep bWweDu2p3rersLX+kOQMsigIlr+dub+v3JpAh8oxNJM18N5qCA2la80/2aUqGjKR 9aBFJrgeN3IoxrFmENckKl8vnNrfLq+5wu+RJFDnZRN/Jm/kLv0erQhxGHewQalo 9/EscVEdYk1k8IkyGY1Vf4e4x71+PjHvVKGFR++oLY8FIB1yKY/T5xbn9DV3uHOI y8rNvjV1SUaZaDjxwCK7EGrCmtyQM31FJOm8tFglRtLvEkhnQ2zEzJAt5zBIqE2L AUt89Z+LVur2hhVKSCjrrhLp+FhA6gGboZswAu6IcVzMwcX/xAUeHOYJbTixxn/J 7i00UezFqbqt6Xeqqas= =Xfy9 -----END PGP SIGNATURE----- --==-=-=-- --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Add-cl-map-into.patch Content-Description: Add cl-map-into >From cebb9a88e244457428385948eaf6bac24a7e5eb1 Mon Sep 17 00:00:00 2001 From: akater Date: Wed, 15 Sep 2021 19:42:47 +0000 Subject: [PATCH] Add cl-map-into map-into is a standard Common Lisp function that acts as cl-map, only values are recorded into a preallocated sequence. * lisp/emacs-lisp/cl-extra.el (cl-map-into): New primary function (cl--map-into-basic-call-arguments-limit, cl--map-into-max-small-signature): New auxiliary constant (cl--map-into-mappers-array, cl--map-into-mappers-alist): New variable (cl--compute-map-into-signature, cl--make-map-into-mapper): New auxiliary function (cl--do-seq-type-signature): New auxiliary macro --- doc/misc/cl.texi | 9 ++ etc/NEWS | 7 + lisp/emacs-lisp/cl-extra.el | 206 +++++++++++++++++++++++++ test/lisp/emacs-lisp/cl-extra-tests.el | 61 ++++++++ 4 files changed, 283 insertions(+) diff --git a/doc/misc/cl.texi b/doc/misc/cl.texi index 04e2c71a2b..45cea26d4e 100644 --- a/doc/misc/cl.texi +++ b/doc/misc/cl.texi @@ -3301,6 +3301,15 @@ be one of the following symbols: @code{vector}, @code{string}, thrown away and @code{cl-map} returns @code{nil}). @end defun +@defun cl-map-into result-sequence function &rest sequences +This function is like @code{cl-map}, except that the values returned by +@var{function} are recorded into existing @var{result-sequence} rather +than into a newly allocated sequence of specified type. Note also that +while @code{cl-map} requires the function to be mapped over to accept at +least one argument, @code{cl-map-into} does not require that from +@var{function}. The return value is @var{result-sequence}. +@end defun + @defun cl-maplist function list &rest more-lists This function calls @var{function} on each of its argument lists, then on the @sc{cdr}s of those lists, and so on, until the diff --git a/etc/NEWS b/etc/NEWS index b91a5cbb72..131df75df4 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -157,6 +157,13 @@ Using 'make-obsolete' on a theme is now supported. This will make ** New function 'define-keymap'. This function allows defining a number of keystrokes with one form. ++++ +** New function 'cl-map-into'. +A counterpart of Common Lisp's 'map-into', this function destructively +modifies a sequence passed to it to contain the results of applying a +function passed to it to each element in the sequences passed to it, +in turn. + +++ ** New macro 'defvar-keymap'. This macro allows defining keymap variables more conveniently. diff --git a/lisp/emacs-lisp/cl-extra.el b/lisp/emacs-lisp/cl-extra.el index 499d26b737..df712e3237 100644 --- a/lisp/emacs-lisp/cl-extra.el +++ b/lisp/emacs-lisp/cl-extra.el @@ -88,6 +88,212 @@ defun cl-equalp (x y) (t (equal x y)))) +;;; map-into + +;; We implement a simple dispatcher for sequence types. +;; +;; cl-extra has cl--mapcar-many for similar purpose. +;; The core issue with it, it goes through args pre-emptively +;; to compute min length when there are more than 2 arguments +;; which makes it and its reverse dependencies fail on circular lists +;; unless there are <3 args. +;; Other issues are +;; - it performs type checks for sequences of known types at runtime +;; - it may cons whole arglist thrice per invocation +;; - looks like it's hard to extend. + +;; Our approach doesn't have these issues. + +(defconst cl--map-into-basic-call-arguments-limit 7 + "Maximal reasonably expected number of arguments to `cl-map-into'. + +`cl-map-into' caches its code corresponding to various signature +types of arglists supplied to `cl-map-into'. Arglists may vary +in length. + +Code corresponding to arglists of length less than +`cl--map-into-basic-call-arguments-limit' is accessed via array. + +Code corresponding to arglists of length greater than or equal to +`cl--map-into-basic-call-arguments-limit' is accessed via alist.") + +(defconst cl--map-into-max-small-signature + (expt 2 cl--map-into-basic-call-arguments-limit) + "Length of array to allocate for caching `cl-map-into' mappers +corresponding to small arglists. + +Such mappers are accessed by their position in an array; position +equals the signature. + +Consider `cl-map-into' arglist + +(result-seq f seq-1 seq-2) + +call-arguments-limit corresponding to arglists of this length or +shorter, is 4 (as there are 4 arguments). This leaves at most 3 +sequences to contribute to type signature. + +Hovewer, we have to store one additional bit for fixnum-based +encoding to be unambiguous and simple. So overall array length +ends up being exactly (expt 2 call-arguments-limit).") + +(defvar cl--map-into-mappers-array + (make-vector cl--map-into-max-small-signature nil) + "Array holding mappers corresponding to small arglists of `cl-map-into'. + +Element type is (or function null).") + +(defvar cl--map-into-mappers-alist nil + "Alist holding mappers corresponding to large arglists of `cl-map-into'.") + +(defun cl--compute-map-into-signature (&rest all-sequences) + "Compute lookup key for `cl-map-into''s almost-arglist ALL-SEQUENCES. + +Namely: ALL-SEQUENCES would be (RESULT-SEQUENCE . SEQUENCES) + for (cl-map-into RESULT-SEQUENCE f . SEQUENCES) + +As a side effect, it checks that ALL-SEQUENCES are of sequence +types. + +Example: +ELISP> (mapcar (lambda (arglist) + (apply #'cl--compute-map-into-signature arglist)) + '(( () () () ) ; signature #b1000 + ( () () [] ) ; signature #b1001 + ( () [] () ) ; signature #b1010 + ( () [] [] ) ; signature #b1011 + ( [] () () ) ; signature #b1100 + )) +(8 9 10 11 12)" + ;; This is not `cl-map-into'-specific and could be used for other caches + ;; which is why we don't specify arglist as (result &rest sequences). + ;; For the time being (while this dispatch is not used widely), + ;; neither docstring nor name reflect this. + (let ((signature 1)) + (dolist (s all-sequences signature) + (setq signature (ash signature 1)) + (cl-etypecase s + (list) + ((or string vector bool-vector) (cl-incf signature)))))) + +(cl-defmacro cl--do-seq-type-signature ((type-var signature &optional result) + &body body) + "With TYPE-VAR bound to a sequence type, evaluate BODY forms, return RESULT. + +TYPE-VAR goes across sequence types in an arglist corresponding +to SIGNATURE that encodes sequence types in that arglist. + +Iteration goes from arglist's end to arglist's start. + +If :first is present at toplevel in BODY, all forms following +it (and those forms only) are evaluated in order when TYPE-VAR is +bound to the first sequence type in the arglist --- which would +be the last sequence type derived from SIGNATURE: see the +previous paragraph. At other iteration steps, only forms +preceding the first :first are evaluated. + +Subsequent instances of toplevel :first in BODY don't affect anything." + (declare (indent 1)) + (let* ((main (cl-copy-list body)) + (first (if (eq :first (car main)) (progn (setf main nil) + (cdr main)) + (cl-loop with sublist = main + while sublist do + (when (eq :first (cadr sublist)) + (setf first (cddr sublist) (cdr sublist) nil) + (cl-return first)) + (pop sublist))))) + (let ((sig (make-symbol "sig"))) + `(let ((,sig ,signature) ,type-var) + ;; (declare (type (integer (1)) ,sig) + ;; ;; Let's keep nil for now. + ;; (type (member nil list vector) ,type-var)) + (cl-check-type ,sig (integer (1))) + (cl-loop (cond + ((or (when (= 2 ,sig) (setq ,type-var 'list)) + (when (= 3 ,sig) (setq ,type-var 'array))) + ;; This duplicates main code sometimes. Maybe, + ;; there is elegant enough way to eliminate duplication. + ,@(or first main) (cl-return ,result)) + (t (setq ,type-var (if (zerop (mod ,sig 2)) + 'list + 'array)) + ,@main)) + (setf ,sig (floor ,sig 2))))))) + +(defun cl--make-map-into-mapper (signature &optional do-not-compile) + "Return mapper for `cl-map-into' specialized on arglists of given SIGNATURE. + +If DO-NOT-COMPILE is nil (default), return byte-compiled function. +Otherwise, return lambda form. + +Example: +ELISP> (cl--make-map-into-mapper #b10101 t) +(lambda (f result-list array-3 list-2 array-1) + (cl-loop for elt in-ref result-list + for elt-3 across array-3 + for elt-2 in list-2 + for elt-1 across array-1 + do (setf elt (funcall f elt-3 elt-2 elt-1)) + finally return result-list))" + (let ((gensym-counter 1) f xs ss loop result-elt result-var) + (cl--do-seq-type-signature (type signature) + (setq loop (nconc (list 'for (let ((it (gensym "elt-"))) + (push it xs) + (cl-decf gensym-counter) + it) + (cl-case type + (list 'in) + (array 'across)) + (let ((it (gensym (format "%s-" type)))) + (push it ss) + it)) + loop)) + :first + (setq loop (nconc (list 'for (setq result-elt (make-symbol "elt")) + (cl-case type + (list 'in-ref) + (array 'across-ref)) + (setq result-var + (make-symbol (format "result-%s" type)))) + loop))) + (funcall + (if do-not-compile #'identity #'byte-compile) + `(lambda ,(cons (setq f (make-symbol "f")) (cons result-var ss)) + (cl-loop ,@(nconc loop `(do (setf ,result-elt (funcall ,f ,@xs)) + ;; Bytecode looks better + ;; with finally return .. + ;; than with finally (cl-return ..). + finally return ,result-var))))))) + +(defun cl-map-into (result-sequence function &rest sequences) + "Map FUNCTION over SEQUENCES, recording values into RESULT-SEQUENCE. + +RESULT-SEQUENCE is destructively modified. + +RESULT-SEQUENCE and each element of SEQUENCES can each be either +a list, a vector, a string, or a bool-vector. If RESULT-SEQUENCE +and each element of SEQUENCES are not all the same length, the +iteration terminates when the shortest sequence (of any of the +SEQUENCES or the RESULT-SEQUENCE) is exhausted. If +RESULT-SEQUENCE is longer than the shortest element of SEQUENCES, +extra elements at the end of RESULT-SEQUENCE are left unchanged. + +Return RESULT-SEQUENCE." + (cl-check-type function function) + (apply + (let* ((sig (apply #'cl--compute-map-into-signature result-sequence + sequences)) + (small (< sig cl--map-into-max-small-signature))) + (with-memoization (if small (aref cl--map-into-mappers-array sig) + ;; TODO: Order alist entries for faster lookup + ;; (note that we'll have to abandon alist-get then). + (alist-get sig cl--map-into-mappers-alist + nil nil #'=)) + (cl--make-map-into-mapper sig))) + function result-sequence sequences)) + + ;;; Control structures. ;;;###autoload diff --git a/test/lisp/emacs-lisp/cl-extra-tests.el b/test/lisp/emacs-lisp/cl-extra-tests.el index 91f0a1e201..a4f21f2edf 100644 --- a/test/lisp/emacs-lisp/cl-extra-tests.el +++ b/test/lisp/emacs-lisp/cl-extra-tests.el @@ -35,6 +35,67 @@ (should (eq (cl-getf plist 'y :none) nil)) (should (eq (cl-getf plist 'z :none) :none)))) +(ert-deftest cl-map-into () + (should (equal '(42 42 42) + (cl-map-into (list 0 0 0) #'+ '(1 2 3) [41 40 39]))) + (should (equal '(42 42 42) + (cl-map-into (list 0 0 0) #'+ [41 40 39] '(1 2 3)))) + (should (equal '(42 42 42) + (cl-map-into (list 0 0 0) #'* '(1 2 3) [42 21 14]))) + (should (equal '(42 42 42) + (let ((s (list 0 0 0))) + (cl-map-into s #'+ '(1 2 3) [41 40 39]) + s))) + (should (equal '(42 42 42) + (let ((s (list 0 0 0))) + (cl-map-into s #'+ s [41 40 39] '(1 2 3)) + s))) + (should (equal '(42 42 42) + (let ((s (list 0 0 0))) + (cl-map-into s #'+ '(1 2 3) s [41 40 39]) + s))) + (should (equal '(42 42 42) + (let ((s (list 0 0 0))) + (cl-map-into s #'+ '(1 2 3) [41 40 39] s) + s))) + (should (equal '(42 42 42) + (let ((s (list 18 19 20))) + (cl-map-into s #'+ s '(6 4 2 1 not-even-a-number) s) + s))) + (should (equal [42 42 42] + (let ((s (vector 0 0 0))) + (cl-map-into s #'+ '(1 2 3) [41 40 39]) + s))) + (should (equal [42 42 42] + (let ((s (vector 0 0 0))) + (cl-map-into s #'+ [41 40 39] '(1 2 3)) + s))) + (should (equal [42 42 42] + (let ((s (vector 18 19 20))) + (cl-map-into s #'+ s '(6 4 2 1 not-even-a-number) s) + s))) + (should (equal "Lisp" + (let ((s (copy-sequence "Loop"))) + (cl-map-into s (lambda (mask new old) (if mask new old)) + (bool-vector nil t t nil) "Disjoint" s) + s))) + (should (equal '(1 2 3) + (let ((s (list 'one 'two 'three))) + (cl-map-into s (let ((n 0)) (lambda () (cl-incf n)))) + s))) + (should (equal (bool-vector t nil t nil t) + (let ((s (bool-vector nil nil nil nil nil))) + (cl-map-into s #'cl-evenp '#1=(0 1 . #1#)) + s))) + (should (equal "GNU GNU GNU GNU" + (let ((cyclically '#1=(t nil . #1#)) + (glue '#1=(?G ?L ?U ?E . #1#)) + (ants '#1=(?A ?N ?T ?\s . #1#)) + (s (make-string 15 0))) + (cl-map-into s (lambda (x y z) (if x y z)) + cyclically glue ants) + s)))) + (ert-deftest cl-extra-test-mapc () (let ((lst '(a b c)) (lst2 '(d e f)) -- 2.32.0 --=-=-=--