commit 87c9d8fcec183c8f4933e218c1895b6a0722b1e1 (HEAD, refs/remotes/origin/master) Author: Nicolas Petton Date: Sat Jun 18 10:09:16 2016 +0200 * lisp/emacs-lisp/map.el (map-contains-key): Better docstring diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el index 7c4afb9..735ab8f 100644 --- a/lisp/emacs-lisp/map.el +++ b/lisp/emacs-lisp/map.el @@ -260,7 +260,7 @@ MAP can be a list, hash-table or array." :hash-table (zerop (hash-table-count map)))) (defun map-contains-key (map key &optional testfn) - "Return non-nil if MAP contain KEY, nil otherwise. + "If MAP contain KEY return KEY, nil otherwise. Equality is defined by TESTFN if non-nil or by `equal' if nil. MAP can be a list, hash-table or array." commit ead28454b89c7ba532ecc3f0f4beac68e943b94d Author: Nicolas Petton Date: Sat Jun 18 10:07:26 2016 +0200 Fix seq-contains Make sure seq-contains return the element of the sequence instead of t. * lisp/emacs-lisp/seq.el (seq-contains): Fix the function. * test/lisp/emacs-lisp/seq-tests.el: Add a regression test. diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index 20610a7..e5004f8 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -4,7 +4,7 @@ ;; Author: Nicolas Petton ;; Keywords: sequences -;; Version: 2.17 +;; Version: 2.18 ;; Package: seq ;; Maintainer: emacs-devel@gnu.org @@ -349,7 +349,8 @@ found or not." "Return the first element in SEQUENCE that is equal to ELT. Equality is defined by TESTFN if non-nil or by `equal' if nil." (seq-some (lambda (e) - (funcall (or testfn #'equal) elt e)) + (when (funcall (or testfn #'equal) elt e) + e)) sequence)) (cl-defgeneric seq-position (sequence elt &optional testfn) diff --git a/test/lisp/emacs-lisp/seq-tests.el b/test/lisp/emacs-lisp/seq-tests.el index b227de3..c2065c6 100644 --- a/test/lisp/emacs-lisp/seq-tests.el +++ b/test/lisp/emacs-lisp/seq-tests.el @@ -181,6 +181,10 @@ Evaluate BODY for each created sequence. (should-not (seq-contains seq 3)) (should-not (seq-contains seq nil)))) +(ert-deftest test-seq-contains-should-return-the-elt () + (with-test-sequences (seq '(3 4 5 6)) + (should (= 5 (seq-contains seq 5))))) + (ert-deftest test-seq-every-p () (with-test-sequences (seq '(43 54 22 1)) (should (seq-every-p (lambda (elt) t) seq)) commit 2aebb0dd1fc66ba8cacef3f734e9a046cbc04ad2 Author: Nicolas Petton Date: Sat Jun 18 09:42:09 2016 +0200 Add new function map-do * lisp/emacs-lisp/map.el (map-do, map--do-alist, map--do-array): New functions. * test/lisp/emacs-lisp/map-tests.el: Add a unit test for map-do. diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el index b97d8b1..7c4afb9 100644 --- a/lisp/emacs-lisp/map.el +++ b/lisp/emacs-lisp/map.el @@ -4,7 +4,7 @@ ;; Author: Nicolas Petton ;; Keywords: convenience, map, hash-table, alist, array -;; Version: 1.0 +;; Version: 1.1 ;; Package: map ;; Maintainer: emacs-devel@gnu.org @@ -201,6 +201,16 @@ MAP can be a list, hash-table or array." function map)) +(defun map-do (function map) + "Apply FUNCTION to each element of MAP and return nil. +FUNCTION.is called with two arguments, the key and the value." + (funcall (map--dispatch map + :list #'map--do-alist + :hash-table #'maphash + :array #'map--do-array) + function + map)) + (defun map-keys-apply (function map) "Return the result of applying FUNCTION to each key of MAP. @@ -354,6 +364,20 @@ MAP can be a list, hash-table or array." (setq index (1+ index)))) map))) +(defun map--do-alist (function alist) + "Private function used to iterate over ALIST using FUNCTION." + (seq-do (lambda (pair) + (funcall function + (car pair) + (cdr pair))) + alist)) + +(defun map--do-array (function array) + "Private function usde to iterate over ARRAY using FUNCTION." + (seq-do-indexed (lambda (elt index) + (funcall function index elt)) + array)) + (defun map--into-hash-table (map) "Convert MAP into a hash-table." (let ((ht (make-hash-table :size (map-length map) diff --git a/test/lisp/emacs-lisp/map-tests.el b/test/lisp/emacs-lisp/map-tests.el index 20cb0f6..0af1c65 100644 --- a/test/lisp/emacs-lisp/map-tests.el +++ b/test/lisp/emacs-lisp/map-tests.el @@ -192,6 +192,14 @@ Evaluate BODY for each created map. (2 . b) (3 . c)))))) +(ert-deftest test-map-do () + (with-maps-do map + (let ((result nil)) + (map-do (lambda (k v) + (add-to-list 'result (list (int-to-string k) v))) + map) + (should (equal result '(("2" 5) ("1" 4) ("0" 3))))))) + (ert-deftest test-map-keys-apply () (with-maps-do map (should (equal (map-keys-apply (lambda (k) (int-to-string k)) commit 9726856f297522b1773e8aadaec34dd1a8e68a14 Author: Nicolas Petton Date: Sat Jun 18 09:32:18 2016 +0200 Add seq-do-indexed * lisp/emacs-lisp/seq.el (seq-do-indexed): New function. * test/lisp/emacs-lisp/seq-tests.el: Add a unit test for seq-do-indexed. diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index 166e886..20610a7 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -4,7 +4,7 @@ ;; Author: Nicolas Petton ;; Keywords: sequences -;; Version: 2.16 +;; Version: 2.17 ;; Package: seq ;; Maintainer: emacs-devel@gnu.org @@ -117,6 +117,16 @@ Return SEQUENCE." (defalias 'seq-each #'seq-do) +(defun seq-do-indexed (function sequence) + "Apply FUNCTION to each element of SEQUENCE and return nil. +Unlike `seq-map', FUNCTION takes two arguments: the element of +the sequence, and its index within the sequence." + (let ((index 0)) + (seq-do (lambda (elt) + (funcall function elt index) + (setq index (1+ index))) + sequence))) + (cl-defgeneric seqp (sequence) "Return non-nil if SEQUENCE is a sequence, nil otherwise." (sequencep sequence)) diff --git a/test/lisp/emacs-lisp/seq-tests.el b/test/lisp/emacs-lisp/seq-tests.el index 50543de..b227de3 100644 --- a/test/lisp/emacs-lisp/seq-tests.el +++ b/test/lisp/emacs-lisp/seq-tests.el @@ -107,6 +107,21 @@ Evaluate BODY for each created sequence. '(a b c d)) '((a 0) (b 1) (c 2) (d 3))))) +(ert-deftest test-seq-do-indexed () + (let ((result nil)) + (seq-do-indexed (lambda (elt i) + (add-to-list 'result (list elt i))) + nil) + (should (equal result nil))) + (with-test-sequences (seq '(4 5 6)) + (let ((result nil)) + (seq-do-indexed (lambda (elt i) + (add-to-list 'result (list elt i))) + seq) + (should (equal (seq-elt result 0) '(6 2))) + (should (equal (seq-elt result 1) '(5 1))) + (should (equal (seq-elt result 2) '(4 0)))))) + (ert-deftest test-seq-filter () (with-test-sequences (seq '(6 7 8 9 10)) (should (equal (seq-filter #'test-sequences-evenp seq) '(6 8 10))) commit 44eca25a4bd1513f08ca380c9f6722c21376ac9f Author: Stefan Monnier Date: Sat Jun 18 00:52:58 2016 -0400 map.el (map-merge*): Use `map-into' at beginning rather than end * lisp/emacs-lisp/map.el (map-merge): Use `map-into' for the first map, and don't use of an intermediate alist. (map-merge-with): Same, plus use `cl-callf' to try and avoid performing 3 lookups per inner iteration. diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el index ba15a65..b97d8b1 100644 --- a/lisp/emacs-lisp/map.el +++ b/lisp/emacs-lisp/map.el @@ -43,6 +43,7 @@ ;;; Code: (require 'seq) +(eval-when-compile (require 'cl-lib)) (pcase-defmacro map (&rest args) "Build a `pcase' pattern matching map elements. @@ -282,27 +283,33 @@ MAP can be a list, hash-table or array." "Merge into a map of type TYPE all the key/value pairs in MAPS. MAP can be a list, hash-table or array." - (let (result) + (let ((result (map-into (pop maps) type))) (while maps + ;; FIXME: When `type' is `list', we get an O(N^2) behavior. + ;; For small tables, this is fine, but for large tables, we + ;; should probably use a hash-table internally which we convert + ;; to an alist in the end. (map-apply (lambda (key value) - (setf (map-elt result key) value)) - (pop maps))) - (map-into result type))) + (setf (map-elt result key) value)) + (pop maps))) + result)) (defun map-merge-with (type function &rest maps) "Merge into a map of type TYPE all the key/value pairs in MAPS. When two maps contain the same key, call FUNCTION on the two values and use the value returned by it. MAP can be a list, hash-table or array." - (let (result) + (let ((result (map-into (pop maps) type)) + (not-found (cons nil nil))) (while maps (map-apply (lambda (key value) - (setf (map-elt result key) - (if (map-contains-key result key) - (funcall function (map-elt result key) value) - value))) - (pop maps))) - (map-into result type))) + (cl-callf (lambda (old) + (if (eq old not-found) + value + (funcall function old value))) + (map-elt result key not-found))) + (pop maps))) + result)) (defun map-into (map type) "Convert the map MAP into a map of type TYPE.