From 17d667b3876920652152baa4eab24134940a0f30 Mon Sep 17 00:00:00 2001 From: Nicolas Petton Date: Wed, 15 Apr 2015 00:33:27 +0200 Subject: Add seq-intersection and seq-difference to the seq library * lisp/emacs-lisp/seq.el (seq-intersection, seq-difference): New functions. * test/automated/seq-tests.el: Add tests for seq-intersection and seq-difference. * doc/lispref/sequences.texi: Add documentation for seq-intersection and seq-difference. --- lisp/emacs-lisp/seq.el | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'lisp/emacs-lisp/seq.el') diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index c5f5906e7e5..6f7f3c46e2a 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -4,7 +4,7 @@ ;; Author: Nicolas Petton ;; Keywords: sequences -;; Version: 1.3 +;; Version: 1.4 ;; Package: seq ;; Maintainer: emacs-devel@gnu.org @@ -240,6 +240,26 @@ negative integer or 0, nil is returned." (setq seq (seq-drop seq n))) (nreverse result)))) +(defun seq-intersection (seq1 seq2 &optional testfn) + "Return a list of the elements that appear in both SEQ1 and SEQ2. +Equality is defined by TESTFN if non-nil or by `equal' if nil." + (seq-reduce (lambda (acc elt) + (if (seq-contains-p seq2 elt testfn) + (cons elt acc) + acc)) + (seq-reverse seq1) + '())) + +(defun seq-difference (seq1 seq2 &optional testfn) + "Return a list of th elements that appear in SEQ1 but not in SEQ2. +Equality is defined by TESTFN if non-nil or by `equal' if nil." + (seq-reduce (lambda (acc elt) + (if (not (seq-contains-p seq2 elt testfn)) + (cons elt acc) + acc)) + (seq-reverse seq1) + '())) + (defun seq-group-by (function seq) "Apply FUNCTION to each element of SEQ. Separate the elements of SEQ into an alist using the results as @@ -318,6 +338,11 @@ This is an optimization for lists in `seq-take-while'." (setq n (+ 1 n))) n)) +(defun seq--activate-font-lock-keywords () + "Activate font-lock keywords for some symbols defined in seq." + (font-lock-add-keywords 'emacs-lisp-mode + '("\\"))) + (defalias 'seq-copy #'copy-sequence) (defalias 'seq-elt #'elt) (defalias 'seq-length #'length) @@ -325,5 +350,7 @@ This is an optimization for lists in `seq-take-while'." (defalias 'seq-each #'seq-do) (defalias 'seq-map #'mapcar) +(add-to-list 'emacs-lisp-mode-hook #'seq--activate-font-lock-keywords) + (provide 'seq) ;;; seq.el ends here -- cgit v1.2.3 From 9760c6cde3f280b2318e9550c2688b1761b09c30 Mon Sep 17 00:00:00 2001 From: Nicolas Petton Date: Sat, 18 Apr 2015 20:10:27 +0200 Subject: * lisp/emacs-lisp/seq.el (seq-concatenate, seq-into): Better error messages. --- lisp/emacs-lisp/seq.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lisp/emacs-lisp/seq.el') diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index 6f7f3c46e2a..320ee201487 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -221,7 +221,7 @@ TYPE must be one of following symbols: vector, string or list. (`vector (apply #'vconcat seqs)) (`string (apply #'concat seqs)) (`list (apply #'append (append seqs '(nil)))) - (t (error "Not a sequence type name: %s" type)))) + (t (error "Not a sequence type name: %S" type)))) (defun seq-mapcat (function seq &optional type) "Concatenate the result of applying FUNCTION to each element of SEQ. @@ -295,7 +295,7 @@ TYPE can be one of the following symbols: vector, string or list." (`vector (vconcat seq)) (`string (concat seq)) (`list (append seq nil)) - (t (error "Not a sequence type name: %s" type)))) + (t (error "Not a sequence type name: %S" type)))) (defun seq--drop-list (list n) "Return a list from LIST without its first N elements. -- cgit v1.2.3 From e224c9465dfe7033b11c0aeb830298c101c9bdcc Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 24 Apr 2015 16:11:35 -0400 Subject: * lisp/emacs-lisp/seq.el (seq-doseq): Tighten the code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (seq-doseq): Fix out-of-scope binding. Don't call `seq-length at every iteration. Reduce `if's from 3 to 2 per iteration. (emacs-lisp-mode-hook): Don't tweak in Emacs≥25. --- doc/lispref/sequences.texi | 2 +- lisp/emacs-lisp/seq.el | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'lisp/emacs-lisp/seq.el') diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index e1330f7d594..b48fae4741f 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -790,7 +790,7 @@ of type @var{type}. @var{type} can be one of the following symbols: @end example @end defun -@defmac seq-doseq (var sequence [result]) body@dots{} +@defmac seq-doseq (var sequence) body@dots{} @cindex sequence iteration This macro is like @code{dolist}, except that @var{sequence} can be a list, vector or string (@pxref{Iteration} for more information about the diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index 320ee201487..b8647ec93ec 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -44,31 +44,28 @@ (defmacro seq-doseq (spec &rest body) "Loop over a sequence. -Similar to `dolist' but can be applied lists, strings and vectors. +Similar to `dolist' but can be applied to lists, strings, and vectors. Evaluate BODY with VAR bound to each element of SEQ, in turn. -Then evaluate RESULT to get return value, default nil. -\(fn (VAR SEQ [RESULT]) BODY...)" +\(fn (VAR SEQ) BODY...)" (declare (indent 1) (debug ((symbolp form &optional form) body))) (let ((is-list (make-symbol "is-list")) (seq (make-symbol "seq")) (index (make-symbol "index"))) `(let* ((,seq ,(cadr spec)) - (,is-list (listp ,seq)) + (,length (if (listp ,seq) nil (seq-length ,seq))) (,index (if ,is-list ,seq 0))) - (while (if ,is-list - (consp ,index) - (< ,index (seq-length ,seq))) - (let ((,(car spec) (if ,is-list - (car ,index) - (seq-elt ,seq ,index)))) - ,@body - (setq ,index (if ,is-list - (cdr ,index) - (+ ,index 1))))) - ,@(if (cddr spec) - `((setq ,(car spec) nil) ,@(cddr spec)))))) + (while (if ,length + (< ,index ,length) + (consp ,index)) + (let ((,(car spec) (if ,length + (prog1 (seq-elt ,seq ,index) + (setq ,index (+ ,index 1))) + (pop ,index)))) + ,@body)) + ;; FIXME: Do we really want to support this? + ,@(cddr spec)))) (defun seq-drop (seq n) "Return a subsequence of SEQ without its first N elements. @@ -350,7 +347,10 @@ This is an optimization for lists in `seq-take-while'." (defalias 'seq-each #'seq-do) (defalias 'seq-map #'mapcar) -(add-to-list 'emacs-lisp-mode-hook #'seq--activate-font-lock-keywords) +(unless (fboundp 'elisp--font-lock-flush-elisp-buffers) + ;; In Emacs≥25, (via elisp--font-lock-flush-elisp-buffers and a few others) + ;; we automatically highlight macros. + (add-to-list 'emacs-lisp-mode-hook #'seq--activate-font-lock-keywords)) (provide 'seq) ;;; seq.el ends here -- cgit v1.2.3 From 7ecda8a22194462114f42225e6d64aaae23c5f6d Mon Sep 17 00:00:00 2001 From: Nicolas Petton Date: Fri, 24 Apr 2015 23:12:50 +0200 Subject: * lisp/emacs-lisp/seq.el (seq-doseq): Fix the macro. --- lisp/emacs-lisp/seq.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lisp/emacs-lisp/seq.el') diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index b8647ec93ec..0050ff0a303 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -50,12 +50,12 @@ Evaluate BODY with VAR bound to each element of SEQ, in turn. \(fn (VAR SEQ) BODY...)" (declare (indent 1) (debug ((symbolp form &optional form) body))) - (let ((is-list (make-symbol "is-list")) + (let ((length (make-symbol "length")) (seq (make-symbol "seq")) (index (make-symbol "index"))) `(let* ((,seq ,(cadr spec)) (,length (if (listp ,seq) nil (seq-length ,seq))) - (,index (if ,is-list ,seq 0))) + (,index (if ,length 0 ,seq))) (while (if ,length (< ,index ,length) (consp ,index)) -- cgit v1.2.3