diff options
| author | Nicolas Petton | 2015-05-05 22:10:32 +0200 |
|---|---|---|
| committer | Nicolas Petton | 2015-05-05 22:10:32 +0200 |
| commit | 4ac426a1b90912ea947d46a57b6fcbbbf7586da1 (patch) | |
| tree | bbf748a53aca52aeac72df288e9cfbac3b951580 | |
| parent | 0508aa26705b3507d9afac54ada4eac47f8cf8a5 (diff) | |
| parent | 8cb4b4f98aa2758a016df25e39ff48cf132ed39c (diff) | |
| download | emacs-4ac426a1b90912ea947d46a57b6fcbbbf7586da1.tar.gz emacs-4ac426a1b90912ea947d46a57b6fcbbbf7586da1.zip | |
Merge branch 'seq-let'
| -rw-r--r-- | doc/lispref/sequences.texi | 38 | ||||
| -rw-r--r-- | lisp/emacs-lisp/seq.el | 48 | ||||
| -rw-r--r-- | test/automated/seq-tests.el | 21 |
3 files changed, 104 insertions, 3 deletions
diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index b48fae4741f..1166ef8b36f 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi | |||
| @@ -413,7 +413,7 @@ but their relative order is also preserved: | |||
| 413 | (9 . "aaa") (9 . "zzz") (9 . "ppp") (9 . "fff")] | 413 | (9 . "aaa") (9 . "zzz") (9 . "ppp") (9 . "fff")] |
| 414 | @end group | 414 | @end group |
| 415 | @end example | 415 | @end example |
| 416 | 416 | ||
| 417 | @xref{Sorting}, for more functions that perform sorting. | 417 | @xref{Sorting}, for more functions that perform sorting. |
| 418 | See @code{documentation} in @ref{Accessing Documentation}, for a | 418 | See @code{documentation} in @ref{Accessing Documentation}, for a |
| 419 | useful example of @code{sort}. | 419 | useful example of @code{sort}. |
| @@ -797,6 +797,42 @@ vector or string (@pxref{Iteration} for more information about the | |||
| 797 | @code{dolist} macro). This is primarily useful for side-effects. | 797 | @code{dolist} macro). This is primarily useful for side-effects. |
| 798 | @end defmac | 798 | @end defmac |
| 799 | 799 | ||
| 800 | @defmac seq-let arguments sequense body@dots{} | ||
| 801 | @cindex sequence destructuring | ||
| 802 | This macro binds the variables in defined in the sequence | ||
| 803 | @var{arguments} to the elements of the sequence @var{sequence}. | ||
| 804 | @var{arguments} can itself include sequences allowing for nested | ||
| 805 | destructuring. | ||
| 806 | |||
| 807 | The @var{arguments} sequence can also include the `&rest' marker | ||
| 808 | followed by a variable name to be bound to the rest of | ||
| 809 | @code{sequence}. | ||
| 810 | |||
| 811 | @example | ||
| 812 | @group | ||
| 813 | (seq-let [first second] [1 2 3 4] | ||
| 814 | (list first second)) | ||
| 815 | @result{} (1 2) | ||
| 816 | @end group | ||
| 817 | @group | ||
| 818 | (seq-let (_ a _ b) '(1 2 3 4) | ||
| 819 | (list a b)) | ||
| 820 | @result{} (2 4) | ||
| 821 | @end group | ||
| 822 | @group | ||
| 823 | (seq-let [a [b [c]]] [1 [2 [3]]] | ||
| 824 | (list a b c)) | ||
| 825 | @result{} (1 2 3) | ||
| 826 | @end group | ||
| 827 | @group | ||
| 828 | (seq-let [a b &rest others] [1 2 3 4] | ||
| 829 | others) | ||
| 830 | @end group | ||
| 831 | @result{} [3 4] | ||
| 832 | @end example | ||
| 833 | @end defmac | ||
| 834 | |||
| 835 | |||
| 800 | @node Arrays | 836 | @node Arrays |
| 801 | @section Arrays | 837 | @section Arrays |
| 802 | @cindex array | 838 | @cindex array |
diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index 456debf5f7c..f1633ce8cd7 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | ;; Author: Nicolas Petton <nicolas@petton.fr> | 5 | ;; Author: Nicolas Petton <nicolas@petton.fr> |
| 6 | ;; Keywords: sequences | 6 | ;; Keywords: sequences |
| 7 | ;; Version: 1.5 | 7 | ;; Version: 1.6 |
| 8 | ;; Package: seq | 8 | ;; Package: seq |
| 9 | 9 | ||
| 10 | ;; Maintainer: emacs-devel@gnu.org | 10 | ;; Maintainer: emacs-devel@gnu.org |
| @@ -40,6 +40,11 @@ | |||
| 40 | ;; | 40 | ;; |
| 41 | ;; All functions are tested in test/automated/seq-tests.el | 41 | ;; All functions are tested in test/automated/seq-tests.el |
| 42 | 42 | ||
| 43 | ;;; TODO: | ||
| 44 | |||
| 45 | ;; - Add a pcase macro named using `pcase-defmacro' that `seq-let' | ||
| 46 | ;; - could wrap. | ||
| 47 | |||
| 43 | ;;; Code: | 48 | ;;; Code: |
| 44 | 49 | ||
| 45 | (defmacro seq-doseq (spec &rest body) | 50 | (defmacro seq-doseq (spec &rest body) |
| @@ -65,6 +70,14 @@ Evaluate BODY with VAR bound to each element of SEQ, in turn. | |||
| 65 | (pop ,index)))) | 70 | (pop ,index)))) |
| 66 | ,@body))))) | 71 | ,@body))))) |
| 67 | 72 | ||
| 73 | (defmacro seq-let (args seq &rest body) | ||
| 74 | "Bind the variables in ARGS to the elements of SEQ then evaluate BODY." | ||
| 75 | (declare (indent 2) (debug t)) | ||
| 76 | (let ((seq-var (make-symbol "seq"))) | ||
| 77 | `(let* ((,seq-var ,seq) | ||
| 78 | ,@(seq--make-bindings args seq-var)) | ||
| 79 | ,@body))) | ||
| 80 | |||
| 68 | (defun seq-drop (seq n) | 81 | (defun seq-drop (seq n) |
| 69 | "Return a subsequence of SEQ without its first N elements. | 82 | "Return a subsequence of SEQ without its first N elements. |
| 70 | The result is a sequence of the same type as SEQ. | 83 | The result is a sequence of the same type as SEQ. |
| @@ -336,7 +349,38 @@ This is an optimization for lists in `seq-take-while'." | |||
| 336 | (defun seq--activate-font-lock-keywords () | 349 | (defun seq--activate-font-lock-keywords () |
| 337 | "Activate font-lock keywords for some symbols defined in seq." | 350 | "Activate font-lock keywords for some symbols defined in seq." |
| 338 | (font-lock-add-keywords 'emacs-lisp-mode | 351 | (font-lock-add-keywords 'emacs-lisp-mode |
| 339 | '("\\<seq-doseq\\>"))) | 352 | '("\\<seq-doseq\\>" "\\<seq-let\\>"))) |
| 353 | |||
| 354 | (defun seq--make-bindings (args seq &optional bindings) | ||
| 355 | "Return a list of bindings of the variables in ARGS to the elements of SEQ. | ||
| 356 | if BINDINGS is non-nil, append new bindings to it, and | ||
| 357 | return BINDINGS." | ||
| 358 | (let ((index 0) | ||
| 359 | (rest-bound nil)) | ||
| 360 | (seq-doseq (name args) | ||
| 361 | (unless rest-bound | ||
| 362 | (pcase name | ||
| 363 | ((pred seq-p) | ||
| 364 | (setq bindings (seq--make-bindings (seq--elt-safe args index) | ||
| 365 | `(seq--elt-safe ,seq ,index) | ||
| 366 | bindings))) | ||
| 367 | (`&rest | ||
| 368 | (progn (push `(,(seq--elt-safe args (1+ index)) | ||
| 369 | (seq-drop ,seq ,index)) | ||
| 370 | bindings) | ||
| 371 | (setq rest-bound t))) | ||
| 372 | (t | ||
| 373 | (push `(,name (seq--elt-safe ,seq ,index)) bindings)))) | ||
| 374 | (setq index (1+ index))) | ||
| 375 | bindings)) | ||
| 376 | |||
| 377 | (defun seq--elt-safe (seq n) | ||
| 378 | "Return element of SEQ at the index N. | ||
| 379 | If no element is found, return nil." | ||
| 380 | (when (or (listp seq) | ||
| 381 | (and (sequencep seq) | ||
| 382 | (> (seq-length seq) n))) | ||
| 383 | (seq-elt seq n))) | ||
| 340 | 384 | ||
| 341 | (defalias 'seq-copy #'copy-sequence) | 385 | (defalias 'seq-copy #'copy-sequence) |
| 342 | (defalias 'seq-elt #'elt) | 386 | (defalias 'seq-elt #'elt) |
diff --git a/test/automated/seq-tests.el b/test/automated/seq-tests.el index 7f6e06cc4b6..ab46eb85f76 100644 --- a/test/automated/seq-tests.el +++ b/test/automated/seq-tests.el | |||
| @@ -276,5 +276,26 @@ Evaluate BODY for each created sequence. | |||
| 276 | (v2 [2 4 6])) | 276 | (v2 [2 4 6])) |
| 277 | (should (seq-empty-p (seq-difference v1 v2))))) | 277 | (should (seq-empty-p (seq-difference v1 v2))))) |
| 278 | 278 | ||
| 279 | (ert-deftest test-seq-let () | ||
| 280 | (with-test-sequences (seq '(1 2 3 4)) | ||
| 281 | (seq-let (a b c d e) seq | ||
| 282 | (should (= a 1)) | ||
| 283 | (should (= b 2)) | ||
| 284 | (should (= c 3)) | ||
| 285 | (should (= d 4)) | ||
| 286 | (should (null e))) | ||
| 287 | (seq-let (a b &rest others) seq | ||
| 288 | (should (= a 1)) | ||
| 289 | (should (= b 2)) | ||
| 290 | (should (same-contents-p others (seq-drop seq 2))))) | ||
| 291 | (let ((seq '(1 (2 (3 (4)))))) | ||
| 292 | (seq-let (_ (_ (_ (a)))) seq | ||
| 293 | (should (= a 4)))) | ||
| 294 | (let (seq) | ||
| 295 | (seq-let (a b c) seq | ||
| 296 | (should (null a)) | ||
| 297 | (should (null b)) | ||
| 298 | (should (null c))))) | ||
| 299 | |||
| 279 | (provide 'seq-tests) | 300 | (provide 'seq-tests) |
| 280 | ;;; seq-tests.el ends here | 301 | ;;; seq-tests.el ends here |