emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/dash 112aa7c251: Fix clients of -compare-fn


From: ELPA Syncer
Subject: [elpa] externals/dash 112aa7c251: Fix clients of -compare-fn
Date: Tue, 7 Jun 2022 06:57:28 -0400 (EDT)

branch: externals/dash
commit 112aa7c251a7cf3e5d024e21f205cdba79df5a33
Author: Basil L. Contovounesios <contovob@tcd.ie>
Commit: Basil L. Contovounesios <contovob@tcd.ie>

    Fix clients of -compare-fn
    
    * NEWS.md (2.19.2): Rename...
    (2.20.0): ...to this.  Announce changes.
    * README.md:
    * dash.texi: Regenerate docs.
    
    * dash.el (-compare-fn): Clarify docstring.
    (dash--member-fn, dash--hash-test-fn, dash--size+): New convenience
    functions.
    (dash--short-list-length): New variable.
    (-distinct, -union, -intersection, -difference): Check for empty
    list early.  Prefer dash--member-fn over -contains? for speed.
    Exclude duplicates from return value.  Use a hash table for long
    lists, but avoid its overhead for short lists.
    (-contains?): Delegate to member if -compare-fn is either equal or
    nil, not just nil.  Reimplement in terms of dash--member-fn.
    (-same-items?): Support multisets of different length.  Use hash
    tables for long lists.
    
    * dev/examples.el (-same-items?): Move from "Predicates" to "Set
    operations".  Extend tests.
    (-contains?, -union, -difference, -intersection, -distinct): Extend
    tests.
    (dash--member-fn, dash--hash-test-fn, dash--size+): New tests.
---
 NEWS.md         |  17 +++-
 README.md       |  94 +++++++++++---------
 dash.el         | 265 +++++++++++++++++++++++++++++++++++++-------------------
 dash.texi       | 114 ++++++++++++------------
 dev/examples.el | 212 +++++++++++++++++++++++++++++++++++++++------
 5 files changed, 492 insertions(+), 210 deletions(-)

diff --git a/NEWS.md b/NEWS.md
index 5dbb24bb26..00f240938e 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -6,12 +6,27 @@ See the end of the file for license conditions.
 
 ## Change log
 
-### From 2.19.1 to 2.19.2
+### From 2.19.1 to 2.20.0
 
 #### Fixes
 
 - Fixed a regression from `2.18` in `-take` that caused it to
   prematurely signal an error on improper lists (#393).
+- The functions `-union`, `-intersection`, and `-difference` now
+  return proper sets, without duplicate elements (#397).
+- The function `-same-items?` now works on multisets (lists with
+  duplicate elements and/or different lengths) (#397).
+
+  For example, the following now returns non-`nil`:
+
+  ```el
+  (-same-items? '(1 1 2 3) '(1 2 3))
+  ```
+
+#### New features
+
+- The function `-contains?` now returns the matching tail of the list
+  instead of just `t`, similarly to `member` (#397).
 
 ### From 2.19.0 to 2.19.1
 
diff --git a/README.md b/README.md
index 90607f65e7..ea48cd53db 100644
--- a/README.md
+++ b/README.md
@@ -222,7 +222,6 @@ Reductions of one or more lists to a boolean value.
 * [`-none?`](#-none-pred-list) `(pred list)`
 * [`-only-some?`](#-only-some-pred-list) `(pred list)`
 * [`-contains?`](#-contains-list-element) `(list element)`
-* [`-same-items?`](#-same-items-list-list2) `(list list2)`
 * [`-is-prefix?`](#-is-prefix-prefix-list) `(prefix list)`
 * [`-is-suffix?`](#-is-suffix-suffix-list) `(suffix list)`
 * [`-is-infix?`](#-is-infix-infix-list) `(infix list)`
@@ -266,12 +265,13 @@ related predicates.
 
 Operations pretending lists are sets.
 
-* [`-union`](#-union-list-list2) `(list list2)`
-* [`-difference`](#-difference-list-list2) `(list list2)`
-* [`-intersection`](#-intersection-list-list2) `(list list2)`
+* [`-union`](#-union-list1-list2) `(list1 list2)`
+* [`-difference`](#-difference-list1-list2) `(list1 list2)`
+* [`-intersection`](#-intersection-list1-list2) `(list1 list2)`
 * [`-powerset`](#-powerset-list) `(list)`
 * [`-permutations`](#-permutations-list) `(list)`
 * [`-distinct`](#-distinct-list) `(list)`
+* [`-same-items?`](#-same-items-list1-list2) `(list1 list2)`
 
 ### Other list operations
 
@@ -1380,28 +1380,15 @@ Alias: `-only-some-p`
 Return non-`nil` if `list` contains `element`.
 
 The test for equality is done with `equal`, or with `-compare-fn`
-if that's non-`nil`.
+if that is non-`nil`.  As with `member`, the return value is
+actually the tail of `list` whose car is `element`.
 
-Alias: `-contains-p`
+Alias: `-contains-p`.
 
 ```el
-(-contains? '(1 2 3) 1) ;; => t
-(-contains? '(1 2 3) 2) ;; => t
-(-contains? '(1 2 3) 4) ;; => nil
-```
-
-#### -same-items? `(list list2)`
-
-Return true if `list` and `list2` has the same items.
-
-The order of the elements in the lists does not matter.
-
-Alias: `-same-items-p`
-
-```el
-(-same-items? '(1 2 3) '(1 2 3)) ;; => t
-(-same-items? '(1 2 3) '(3 2 1)) ;; => t
-(-same-items? '(1 2 3) '(1 2 3 4)) ;; => nil
+(-contains? '(1 2 3) 1) ;; => (1 2 3)
+(-contains? '(1 2 3) 2) ;; => (2 3)
+(-contains? '(1 2 3) 4) ;; => ()
 ```
 
 #### -is-prefix? `(prefix list)`
@@ -1774,23 +1761,25 @@ permutation to `list` sorts it in descending order.
 
 Operations pretending lists are sets.
 
-#### -union `(list list2)`
+#### -union `(list1 list2)`
+
+Return a new list of distinct elements appearing in either `list1` or `list2`.
 
-Return a new list of all elements appearing in either `list1` or `list2`.
-Equality is defined by the value of `-compare-fn` if non-`nil`;
-otherwise `equal`.
+The test for equality is done with `equal`, or with `-compare-fn`
+if that is non-`nil`.
 
 ```el
 (-union '(1 2 3) '(3 4 5)) ;; => (1 2 3 4 5)
-(-union '(1 2 3 4) ()) ;; => (1 2 3 4)
-(-union '(1 1 2 2) '(3 2 1)) ;; => (1 1 2 2 3)
+(-union '(1 2 2 4) ()) ;; => (1 2 4)
+(-union '(1 1 2 2) '(4 4 3 2 1)) ;; => (1 2 4 3)
 ```
 
-#### -difference `(list list2)`
+#### -difference `(list1 list2)`
+
+Return a new list with the distinct members of `list1` that are not in `list2`.
 
-Return a new list with only the members of `list` that are not in `list2`.
-The test for equality is done with `equal`,
-or with `-compare-fn` if that's non-`nil`.
+The test for equality is done with `equal`, or with `-compare-fn`
+if that is non-`nil`.
 
 ```el
 (-difference () ()) ;; => ()
@@ -1798,16 +1787,17 @@ or with `-compare-fn` if that's non-`nil`.
 (-difference '(1 2 3 4) '(3 4 5 6)) ;; => (1 2)
 ```
 
-#### -intersection `(list list2)`
+#### -intersection `(list1 list2)`
 
-Return a new list of the elements appearing in both `list1` and `list2`.
-Equality is defined by the value of `-compare-fn` if non-`nil`;
-otherwise `equal`.
+Return a new list of distinct elements appearing in both `list1` and `list2`.
+
+The test for equality is done with `equal`, or with `-compare-fn`
+if that is non-`nil`.
 
 ```el
 (-intersection () ()) ;; => ()
 (-intersection '(1 2 3) '(4 5 6)) ;; => ()
-(-intersection '(1 2 3 4) '(3 4 5 6)) ;; => (3 4)
+(-intersection '(1 2 2 3) '(4 3 3 2)) ;; => (2 3)
 ```
 
 #### -powerset `(list)`
@@ -1831,18 +1821,36 @@ Return the permutations of `list`.
 
 #### -distinct `(list)`
 
-Return a new list with all duplicates removed.
-The test for equality is done with `equal`,
-or with `-compare-fn` if that's non-`nil`.
+Return a copy of `list` with all duplicate elements removed.
+
+The test for equality is done with `equal`, or with `-compare-fn`
+if that is non-`nil`.
 
-Alias: `-uniq`
+Alias: `-uniq`.
 
 ```el
 (-distinct ()) ;; => ()
-(-distinct '(1 2 2 4)) ;; => (1 2 4)
+(-distinct '(1 1 2 3 3)) ;; => (1 2 3)
 (-distinct '(t t t)) ;; => (t)
 ```
 
+#### -same-items? `(list1 list2)`
+
+Return non-`nil` if `list1` and `list2` have the same distinct elements.
+
+The order of the elements in the lists does not matter.  The
+lists may be of different lengths, i.e., contain duplicate
+elements.  The test for equality is done with `equal`, or with
+`-compare-fn` if that is non-`nil`.
+
+Alias: `-same-items-p`.
+
+```el
+(-same-items? '(1 2 3) '(1 2 3)) ;; => t
+(-same-items? '(1 1 2 3) '(3 3 2 1)) ;; => t
+(-same-items? '(1 2 3) '(1 2 3 4)) ;; => nil
+```
+
 ## Other list operations
 
 Other list functions not fit to be classified elsewhere.
diff --git a/dash.el b/dash.el
index 7a90e7ba25..739e4864ae 100644
--- a/dash.el
+++ b/dash.el
@@ -2690,67 +2690,146 @@ execute body."
            (indent 1))
   `(--if-let ,val (progn ,@body)))
 
+;; TODO: Get rid of this dynamic variable, passing it as an argument
+;; instead?
 (defvar -compare-fn nil
-  "Tests for equality use this function or `equal' if this is nil.
-It should only be set using dynamic scope with a let, like:
-
-  (let ((-compare-fn #\\='=)) (-union numbers1 numbers2 numbers3)")
+  "Tests for equality use this function, or `equal' if this is nil.
+
+As a dynamic variable, this should be temporarily bound around
+the relevant operation, rather than permanently modified.  For
+example:
+
+  (let ((-compare-fn #\\='=))
+    (-union \\='(1 2 3) \\='(2 3 4)))")
+
+(defun dash--member-fn ()
+  "Return the flavor of `member' that goes best with `-compare-fn'."
+  (declare (side-effect-free error-free))
+  (let ((cmp -compare-fn))
+    (cond ((memq cmp '(nil equal)) #'member)
+          ((eq cmp #'eq) #'memq)
+          ((eq cmp #'eql) #'memql)
+          ((lambda (elt list)
+             (while (and list (not (funcall cmp elt (car list))))
+               (pop list))
+             list)))))
+
+(defun dash--hash-test-fn ()
+  "Return the hash table test function corresponding to `-compare-fn'.
+Return nil if `-compare-fn' is not a known test function."
+  (declare (side-effect-free error-free))
+  ;; In theory this could also recognize values that are custom
+  ;; `hash-table-test's, but too often the :test name is different
+  ;; from the equality function, so it doesn't seem worthwile.
+  (car (memq (or -compare-fn #'equal) '(equal eq eql))))
+
+(defvar dash--short-list-length 32
+  "Maximum list length considered short, for optimizations.
+For example, the speedup afforded by hash table lookup may start
+to outweigh its runtime and memory overhead for problem sizes
+greater than this value.  See also the discussion in PR #305.")
 
 (defun -distinct (list)
-  "Return a new list with all duplicates removed.
-The test for equality is done with `equal',
-or with `-compare-fn' if that's non-nil.
-
-Alias: `-uniq'"
-  ;; Implementation note: The speedup gained from hash table lookup
-  ;; starts to outweigh its overhead for lists of length greater than
-  ;; 32.  See discussion in PR #305.
-  (let* ((len (length list))
-         (lut (and (> len 32)
-                   ;; Check that `-compare-fn' is a valid hash-table
-                   ;; lookup function or nil.
-                   (memq -compare-fn '(nil equal eq eql))
-                   (make-hash-table :test (or -compare-fn #'equal)
-                                    :size len))))
-    (if lut
-        (--filter (unless (gethash it lut)
-                    (puthash it t lut))
-                  list)
-      (--each list (unless (-contains? lut it) (!cons it lut)))
-      (nreverse lut))))
-
-(defalias '-uniq '-distinct)
-
-(defun -union (list list2)
-  "Return a new list of all elements appearing in either LIST1 or LIST2.
-Equality is defined by the value of `-compare-fn' if non-nil;
-otherwise `equal'."
-  ;; We fall back to iteration implementation if the comparison
-  ;; function isn't one of `eq', `eql' or `equal'.
-  (let* ((result (reverse list))
-         ;; TODO: get rid of this dynamic variable, pass it as an
-         ;; argument instead.
-         (-compare-fn (if (bound-and-true-p -compare-fn)
-                          -compare-fn
-                        'equal)))
-    (if (memq -compare-fn '(eq eql equal))
-        (let ((ht (make-hash-table :test -compare-fn)))
-          (--each list (puthash it t ht))
-          (--each list2 (unless (gethash it ht) (!cons it result))))
-      (--each list2 (unless (-contains? result it) (!cons it result))))
-    (nreverse result)))
+  "Return a copy of LIST with all duplicate elements removed.
+
+The test for equality is done with `equal', or with `-compare-fn'
+if that is non-nil.
+
+Alias: `-uniq'."
+  (let (test len)
+    (cond ((null list) ())
+          ;; Use a hash table if `-compare-fn' is a known hash table
+          ;; test function and the list is long enough.
+          ((and (setq test (dash--hash-test-fn))
+                (> (setq len (length list)) dash--short-list-length))
+           (let ((ht (make-hash-table :test test :size len)))
+             (--filter (unless (gethash it ht) (puthash it t ht)) list)))
+          ((let ((member (dash--member-fn)) uniq)
+             (--each list (unless (funcall member it uniq) (push it uniq)))
+             (nreverse uniq))))))
+
+(defalias '-uniq #'-distinct)
+
+(defun dash--size+ (size1 size2)
+  "Return the sum of nonnegative fixnums SIZE1 and SIZE2.
+Return `most-positive-fixnum' on overflow.  This ensures the
+result is a valid size, particularly for allocating hash tables,
+even in the presence of bignum support."
+  (declare (side-effect-free t))
+  (if (< size1 (- most-positive-fixnum size2))
+      (+ size1 size2)
+    most-positive-fixnum))
+
+(defun -union (list1 list2)
+  "Return a new list of distinct elements appearing in either LIST1 or LIST2.
 
-(defun -intersection (list list2)
-  "Return a new list of the elements appearing in both LIST1 and LIST2.
-Equality is defined by the value of `-compare-fn' if non-nil;
-otherwise `equal'."
-  (--filter (-contains? list2 it) list))
+The test for equality is done with `equal', or with `-compare-fn'
+if that is non-nil."
+  (let ((lists (list list1 list2)) test len union)
+    (cond ((null (or list1 list2)))
+          ;; Use a hash table if `-compare-fn' is a known hash table
+          ;; test function and the lists are long enough.
+          ((and (setq test (dash--hash-test-fn))
+                (> (setq len (dash--size+ (length list1) (length list2)))
+                   dash--short-list-length))
+           (let ((ht (make-hash-table :test test :size len)))
+             (dolist (l lists)
+               (--each l (unless (gethash it ht)
+                           (puthash it t ht)
+                           (push it union))))))
+          ((let ((member (dash--member-fn)))
+             (dolist (l lists)
+               (--each l (unless (funcall member it union) (push it 
union)))))))
+    (nreverse union)))
+
+(defun -intersection (list1 list2)
+  "Return a new list of distinct elements appearing in both LIST1 and LIST2.
+
+The test for equality is done with `equal', or with `-compare-fn'
+if that is non-nil."
+  (let (test len)
+    (cond ((null (and list1 list2)) ())
+          ;; Use a hash table if `-compare-fn' is a known hash table
+          ;; test function and either list is long enough.
+          ((and (setq test (dash--hash-test-fn))
+                (> (setq len (length list2)) dash--short-list-length))
+           (let ((ht (make-hash-table :test test :size len)))
+             (--each list2 (puthash it t ht))
+             ;; Remove visited elements to avoid duplicates.
+             (--filter (when (gethash it ht) (remhash it ht) t) list1)))
+          ((let ((member (dash--member-fn)) intersection)
+             (--each list1 (and (funcall member it list2)
+                                (not (funcall member it intersection))
+                                (push it intersection)))
+             (nreverse intersection))))))
+
+(defun -difference (list1 list2)
+  "Return a new list with the distinct members of LIST1 that are not in LIST2.
 
-(defun -difference (list list2)
-  "Return a new list with only the members of LIST that are not in LIST2.
-The test for equality is done with `equal',
-or with `-compare-fn' if that's non-nil."
-  (--filter (not (-contains? list2 it)) list))
+The test for equality is done with `equal', or with `-compare-fn'
+if that is non-nil."
+  (let (test len1 len2)
+    (cond ((null list1) ())
+          ((null list2) (-distinct list1))
+          ;; Use a hash table if `-compare-fn' is a known hash table
+          ;; test function and the subtrahend is long enough.
+          ((and (setq test (dash--hash-test-fn))
+                (setq len1 (length list1))
+                (setq len2 (length list2))
+                (> (max len1 len2) dash--short-list-length))
+           (let ((ht1 (make-hash-table :test test :size len1))
+                 (ht2 (make-hash-table :test test :size len2)))
+             (--each list2 (puthash it t ht2))
+             ;; Avoid duplicates by tracking visited items in `ht1'.
+             (--filter (unless (or (gethash it ht2) (gethash it ht1))
+                         (puthash it t ht1))
+                       list1)))
+          ((let ((member (dash--member-fn)) difference)
+             (--each list1
+               (unless (or (funcall member it list2)
+                           (funcall member it difference))
+                 (push it difference)))
+             (nreverse difference))))))
 
 (defun -powerset (list)
   "Return the power set of LIST."
@@ -2794,37 +2873,49 @@ or with `-compare-fn' if that's non-nil."
   "Return non-nil if LIST contains ELEMENT.
 
 The test for equality is done with `equal', or with `-compare-fn'
-if that's non-nil.
-
-Alias: `-contains-p'"
-  (not
-   (null
-    (cond
-     ((null -compare-fn)    (member element list))
-     ((eq -compare-fn 'eq)  (memq element list))
-     ((eq -compare-fn 'eql) (memql element list))
-     (t
-      (let ((lst list))
-        (while (and lst
-                    (not (funcall -compare-fn element (car lst))))
-          (setq lst (cdr lst)))
-        lst))))))
-
-(defalias '-contains-p '-contains?)
-
-(defun -same-items? (list list2)
-  "Return true if LIST and LIST2 has the same items.
-
-The order of the elements in the lists does not matter.
-
-Alias: `-same-items-p'"
-  (let ((length-a (length list))
-        (length-b (length list2)))
-    (and
-     (= length-a length-b)
-     (= length-a (length (-intersection list list2))))))
-
-(defalias '-same-items-p '-same-items?)
+if that is non-nil.  As with `member', the return value is
+actually the tail of LIST whose car is ELEMENT.
+
+Alias: `-contains-p'."
+  (funcall (dash--member-fn) element list))
+
+(defalias '-contains-p #'-contains?)
+
+(defun -same-items? (list1 list2)
+  "Return non-nil if LIST1 and LIST2 have the same distinct elements.
+
+The order of the elements in the lists does not matter.  The
+lists may be of different lengths, i.e., contain duplicate
+elements.  The test for equality is done with `equal', or with
+`-compare-fn' if that is non-nil.
+
+Alias: `-same-items-p'."
+  (let (test len1 len2)
+    (cond ((null (or list1 list2)))
+          ((null (and list1 list2)) nil)
+          ;; Use a hash table if `-compare-fn' is a known hash table
+          ;; test function and either list is long enough.
+          ((and (setq test (dash--hash-test-fn))
+                (setq len1 (length list1))
+                (setq len2 (length list2))
+                (> (max len1 len2) dash--short-list-length))
+           (let ((ht1 (make-hash-table :test test :size len1))
+                 (ht2 (make-hash-table :test test :size len2)))
+             (--each list1 (puthash it t ht1))
+             ;; Move visited elements from `ht1' to `ht2'.  This way,
+             ;; if visiting all of `list2' leaves `ht1' empty, then
+             ;; all elements from both lists have been accounted for.
+             (and (--every (cond ((gethash it ht1)
+                                  (remhash it ht1)
+                                  (puthash it t ht2))
+                                 ((gethash it ht2)))
+                           list2)
+                  (zerop (hash-table-count ht1)))))
+          ((let ((member (dash--member-fn)))
+             (and (--all? (funcall member it list2) list1)
+                  (--all? (funcall member it list1) list2)))))))
+
+(defalias '-same-items-p #'-same-items?)
 
 (defun -is-prefix? (prefix list)
   "Return non-nil if PREFIX is a prefix of LIST.
diff --git a/dash.texi b/dash.texi
index 919487b2ef..72ecbe65a8 100644
--- a/dash.texi
+++ b/dash.texi
@@ -1884,46 +1884,23 @@ Alias: @code{-only-some-p}
 Return non-@code{nil} if @var{list} contains @var{element}.
 
 The test for equality is done with @code{equal}, or with @code{-compare-fn}
-if that's non-@code{nil}.
+if that is non-@code{nil}.  As with @code{member}, the return value is
+actually the tail of @var{list} whose car is @var{element}.
 
-Alias: @code{-contains-p}
+Alias: @code{-contains-p}.
 
 @example
 @group
 (-contains? '(1 2 3) 1)
-    @result{} t
+    @result{} (1 2 3)
 @end group
 @group
 (-contains? '(1 2 3) 2)
-    @result{} t
+    @result{} (2 3)
 @end group
 @group
 (-contains? '(1 2 3) 4)
-    @result{} nil
-@end group
-@end example
-@end defun
-
-@anchor{-same-items?}
-@defun -same-items? (list list2)
-Return true if @var{list} and @var{list2} has the same items.
-
-The order of the elements in the lists does not matter.
-
-Alias: @code{-same-items-p}
-
-@example
-@group
-(-same-items? '(1 2 3) '(1 2 3))
-    @result{} t
-@end group
-@group
-(-same-items? '(1 2 3) '(3 2 1))
-    @result{} t
-@end group
-@group
-(-same-items? '(1 2 3) '(1 2 3 4))
-    @result{} nil
+    @result{} ()
 @end group
 @end example
 @end defun
@@ -2566,10 +2543,11 @@ permutation to @var{list} sorts it in descending order.
 Operations pretending lists are sets.
 
 @anchor{-union}
-@defun -union (list list2)
-Return a new list of all elements appearing in either @var{list1} or 
@var{list2}.
-Equality is defined by the value of @code{-compare-fn} if non-@code{nil};
-otherwise @code{equal}.
+@defun -union (list1 list2)
+Return a new list of distinct elements appearing in either @var{list1} or 
@var{list2}.
+
+The test for equality is done with @code{equal}, or with @code{-compare-fn}
+if that is non-@code{nil}.
 
 @example
 @group
@@ -2577,21 +2555,22 @@ otherwise @code{equal}.
     @result{} (1 2 3 4 5)
 @end group
 @group
-(-union '(1 2 3 4) ())
-    @result{} (1 2 3 4)
+(-union '(1 2 2 4) ())
+    @result{} (1 2 4)
 @end group
 @group
-(-union '(1 1 2 2) '(3 2 1))
-    @result{} (1 1 2 2 3)
+(-union '(1 1 2 2) '(4 4 3 2 1))
+    @result{} (1 2 4 3)
 @end group
 @end example
 @end defun
 
 @anchor{-difference}
-@defun -difference (list list2)
-Return a new list with only the members of @var{list} that are not in 
@var{list2}.
-The test for equality is done with @code{equal},
-or with @code{-compare-fn} if that's non-@code{nil}.
+@defun -difference (list1 list2)
+Return a new list with the distinct members of @var{list1} that are not in 
@var{list2}.
+
+The test for equality is done with @code{equal}, or with @code{-compare-fn}
+if that is non-@code{nil}.
 
 @example
 @group
@@ -2610,10 +2589,11 @@ or with @code{-compare-fn} if that's non-@code{nil}.
 @end defun
 
 @anchor{-intersection}
-@defun -intersection (list list2)
-Return a new list of the elements appearing in both @var{list1} and 
@var{list2}.
-Equality is defined by the value of @code{-compare-fn} if non-@code{nil};
-otherwise @code{equal}.
+@defun -intersection (list1 list2)
+Return a new list of distinct elements appearing in both @var{list1} and 
@var{list2}.
+
+The test for equality is done with @code{equal}, or with @code{-compare-fn}
+if that is non-@code{nil}.
 
 @example
 @group
@@ -2625,8 +2605,8 @@ otherwise @code{equal}.
     @result{} ()
 @end group
 @group
-(-intersection '(1 2 3 4) '(3 4 5 6))
-    @result{} (3 4)
+(-intersection '(1 2 2 3) '(4 3 3 2))
+    @result{} (2 3)
 @end group
 @end example
 @end defun
@@ -2669,11 +2649,12 @@ Return the permutations of @var{list}.
 
 @anchor{-distinct}
 @defun -distinct (list)
-Return a new list with all duplicates removed.
-The test for equality is done with @code{equal},
-or with @code{-compare-fn} if that's non-@code{nil}.
+Return a copy of @var{list} with all duplicate elements removed.
+
+The test for equality is done with @code{equal}, or with @code{-compare-fn}
+if that is non-@code{nil}.
 
-Alias: @code{-uniq}
+Alias: @code{-uniq}.
 
 @example
 @group
@@ -2681,8 +2662,8 @@ Alias: @code{-uniq}
     @result{} ()
 @end group
 @group
-(-distinct '(1 2 2 4))
-    @result{} (1 2 4)
+(-distinct '(1 1 2 3 3))
+    @result{} (1 2 3)
 @end group
 @group
 (-distinct '(t t t))
@@ -2691,6 +2672,33 @@ Alias: @code{-uniq}
 @end example
 @end defun
 
+@anchor{-same-items?}
+@defun -same-items? (list1 list2)
+Return non-@code{nil} if @var{list1} and @var{list2} have the same distinct 
elements.
+
+The order of the elements in the lists does not matter.  The
+lists may be of different lengths, i.e., contain duplicate
+elements.  The test for equality is done with @code{equal}, or with
+@code{-compare-fn} if that is non-@code{nil}.
+
+Alias: @code{-same-items-p}.
+
+@example
+@group
+(-same-items? '(1 2 3) '(1 2 3))
+    @result{} t
+@end group
+@group
+(-same-items? '(1 1 2 3) '(3 3 2 1))
+    @result{} t
+@end group
+@group
+(-same-items? '(1 2 3) '(1 2 3 4))
+    @result{} nil
+@end group
+@end example
+@end defun
+
 @node Other list operations
 @section Other list operations
 
diff --git a/dev/examples.el b/dev/examples.el
index 9231c739c5..086dc15777 100644
--- a/dev/examples.el
+++ b/dev/examples.el
@@ -27,6 +27,7 @@
 
 (require 'dash)
 (require 'dash-defs "dev/dash-defs")
+(require 'ert)
 
 (eval-when-compile
   ;; TODO: Emacs 24.3 first introduced `setf', so remove this when
@@ -771,18 +772,21 @@ value rather than consuming a list to produce a single 
value."
     (--only-some? (> it 2) '(1 2 3)) => t)
 
   (defexamples -contains?
-    (-contains? '(1 2 3) 1) => t
-    (-contains? '(1 2 3) 2) => t
-    (-contains? '(1 2 3) 4) => nil
-    (-contains? '() 1) => nil
-    (-contains? '() '()) => nil)
-
-  (defexamples -same-items?
-    (-same-items? '(1 2 3) '(1 2 3)) => t
-    (-same-items? '(1 2 3) '(3 2 1)) => t
-    (-same-items? '(1 2 3) '(1 2 3 4)) => nil
-    (-same-items? '((a . 1) (b . 2)) '((a . 1) (b . 2))) => t
-    (-same-items? '(1 2 3) '(2 3 1)) => t)
+    (-contains? '(1 2 3) 1) => '(1 2 3)
+    (-contains? '(1 2 3) 2) => '(2 3)
+    (-contains? '(1 2 3) 4) => '()
+    (-contains? '() 1) => '()
+    (-contains? '() '()) => '()
+    (-contains? `(,(string ?a)) "a") => '("a")
+    (-contains? '(a a) 'a) => '(a a)
+    (-contains? '(b b a a) 'a) => '(a a)
+    (-contains? '(a a b b) 'a) => '(a a b b)
+    (let ((-compare-fn #'eq)) (-contains? `(,(string ?a)) "a")) => '()
+    (let ((-compare-fn #'string=)) (-contains? '(a) 'b)) => '()
+    (let ((-compare-fn #'string=)) (-contains? '(a) "a")) => '(a)
+    (let ((-compare-fn #'string=)) (-contains? '("a") 'a)) => '("a")
+    (let ((-compare-fn #'string=)) (-contains? '(a "a") 'a)) => '(a "a")
+    (let ((-compare-fn #'string=)) (-contains? '("a" a) 'a)) => '("a" a))
 
   (defexamples -is-prefix?
     (-is-prefix? '(1 2 3) '(1 2 3 4 5)) => t
@@ -1112,18 +1116,77 @@ related predicates."
 
   (defexamples -union
     (-union '(1 2 3) '(3 4 5))  => '(1 2 3 4 5)
-    (-union '(1 2 3 4) '())  => '(1 2 3 4)
-    (-union '(1 1 2 2) '(3 2 1))  => '(1 1 2 2 3))
+    (-union '(1 2 2 4) '())  => '(1 2 4)
+    (-union '(1 1 2 2) '(4 4 3 2 1))  => '(1 2 4 3)
+    (-union '() '()) => '()
+    (-union '() '(a)) => '(a)
+    (-union '() '(a a)) => '(a)
+    (-union '() '(a a b)) => '(a b)
+    (-union '() '(a b a)) => '(a b)
+    (-union '() '(b a a)) => '(b a)
+    (-union '(a) '()) => '(a)
+    (-union '(a a) '()) => '(a)
+    (-union '(a a b) '()) => '(a b)
+    (-union '(a b a) '()) => '(a b)
+    (-union '(b a a) '()) => '(b a)
+    (let ((dash--short-list-length 0)) (-union '() '(a))) => '(a)
+    (let ((dash--short-list-length 0)) (-union '() '(a a))) => '(a)
+    (let ((dash--short-list-length 0)) (-union '() '(a a b))) => '(a b)
+    (let ((dash--short-list-length 0)) (-union '() '(a b a))) => '(a b)
+    (let ((dash--short-list-length 0)) (-union '() '(b a a))) => '(b a)
+    (let ((dash--short-list-length 0)) (-union '(a) '())) => '(a)
+    (let ((dash--short-list-length 0)) (-union '(a a) '())) => '(a)
+    (let ((dash--short-list-length 0)) (-union '(a a b) '())) => '(a b)
+    (let ((dash--short-list-length 0)) (-union '(a b a) '())) => '(a b)
+    (let ((dash--short-list-length 0)) (-union '(b a a) '())) => '(b a)
+    (let ((dash--short-list-length 0)) (-union '(a a b c c) '(e e d c b)))
+    => '(a b c e d)
+    (let ((-compare-fn #'string=)) (-union '(a "b") '("a" b))) => '(a "b")
+    (let ((-compare-fn #'string=)) (-union '("a" b) '(a "b"))) => '("a" b))
 
   (defexamples -difference
     (-difference '() '()) => '()
     (-difference '(1 2 3) '(4 5 6)) => '(1 2 3)
-    (-difference '(1 2 3 4) '(3 4 5 6)) => '(1 2))
+    (-difference '(1 2 3 4) '(3 4 5 6)) => '(1 2)
+    (-difference '() '(a)) => '()
+    (-difference '(a) '()) => '(a)
+    (-difference '(a) '(a)) => '()
+    (-difference '(a a) '()) => '(a)
+    (-difference '(a a) '(a)) => '()
+    (-difference '(a a) '(a a)) => '()
+    (-difference '(a a) '(b)) => '(a)
+    (-difference '(a b c c d a) '(c c b)) => '(a d)
+    (let ((dash--short-list-length 0)) (-difference '(a) '(a))) => '()
+    (let ((dash--short-list-length 0)) (-difference '(a a) '(a))) => '()
+    (let ((dash--short-list-length 0)) (-difference '(a a) '(a a))) => '()
+    (let ((dash--short-list-length 0)) (-difference '(a a) '(b))) => '(a)
+    (let ((dash--short-list-length 0)) (-difference '(a b c c d a) '(c c b)))
+    => '(a d)
+    (let ((-compare-fn #'string=)) (-difference '(a) '("a"))) => '()
+    (let ((-compare-fn #'string=)) (-difference '("a") '(a))) => '()
+    (let ((-compare-fn #'string=)) (-difference '(a "a") '(a))) => '()
+    (let ((-compare-fn #'string=)) (-difference '(a "a") '(b))) => '(a)
+    (let ((-compare-fn #'string=)) (-difference '("a") '(a a))) => '())
 
   (defexamples -intersection
     (-intersection '() '()) => '()
     (-intersection '(1 2 3) '(4 5 6)) => '()
-    (-intersection '(1 2 3 4) '(3 4 5 6)) => '(3 4))
+    (-intersection '(1 2 2 3) '(4 3 3 2)) => '(2 3)
+    (-intersection '() '(a)) => '()
+    (-intersection '(a) '()) => '()
+    (-intersection '(a) '(a)) => '(a)
+    (-intersection '(a a b) '(b a)) => '(a b)
+    (-intersection '(a b) '(b a a)) => '(a b)
+    (let ((dash--short-list-length 0)) (-intersection '(a) '(b))) => '()
+    (let ((dash--short-list-length 0)) (-intersection '(a) '(a))) => '(a)
+    (let ((dash--short-list-length 0)) (-intersection '(a a b) '(b b a)))
+    => '(a b)
+    (let ((dash--short-list-length 0)) (-intersection '(a a b) '(b a)))
+    => '(a b)
+    (let ((dash--short-list-length 0)) (-intersection '(a b) '(b a a)))
+    => '(a b)
+    (let ((-compare-fn #'string=)) (-intersection '(a) '("a")) => '(a))
+    (let ((-compare-fn #'string=)) (-intersection '("a") '(a)) => '("a")))
 
   (defexamples -powerset
     (-powerset '()) => '(nil)
@@ -1136,19 +1199,73 @@ related predicates."
 
   (defexamples -distinct
     (-distinct '()) => '()
-    (-distinct '(1 2 2 4)) => '(1 2 4)
+    (-distinct '(1 1 2 3 3)) => '(1 2 3)
     (-distinct '(t t t)) => '(t)
     (-distinct '(nil nil nil)) => '(nil)
-    (let ((-compare-fn nil))
-      (-distinct '((1) (2) (1) (1)))) => '((1) (2))
-    (let ((-compare-fn #'eq))
-      (-distinct '((1) (2) (1) (1)))) => '((1) (2) (1) (1))
-    (let ((-compare-fn #'eq))
-      (-distinct '(:a :b :a :a))) => '(:a :b)
-    (let ((-compare-fn #'eql))
-      (-distinct '(2.1 3.1 2.1 2.1))) => '(2.1 3.1)
+    (-uniq '((1) (2) (1) (1))) => '((1) (2))
+    (let ((-compare-fn #'eq)) (-uniq '((1) (2) (1) (1)))) => '((1) (2) (1) (1))
+    (let ((-compare-fn #'eq)) (-uniq '(:a :b :a :a))) => '(:a :b)
+    (let ((-compare-fn #'eql)) (-uniq '(2.1 3.1 2.1 2.1))) => '(2.1 3.1)
     (let ((-compare-fn #'string=))
-      (-distinct '(dash "dash" "ash" "cash" "bash"))) => '(dash "ash" "cash" 
"bash")))
+      (-uniq '(dash "dash" "ash" "cash" "bash")))
+    => '(dash "ash" "cash" "bash")
+    (let ((-compare-fn #'string=)) (-uniq '(a))) => '(a)
+    (let ((-compare-fn #'string=)) (-uniq '(a a))) => '(a)
+    (let ((-compare-fn #'string=)) (-uniq '(a b))) => '(a b)
+    (let ((-compare-fn #'string=)) (-uniq '(b a))) => '(b a)
+    (let ((-compare-fn #'string=)) (-uniq '(a "a"))) => '(a)
+    (let ((-compare-fn #'string=)) (-uniq '("a" a))) => '("a")
+    (let ((dash--short-list-length 0)) (-uniq '(a))) => '(a)
+    (let ((dash--short-list-length 0)) (-uniq '(a b))) => '(a b)
+    (let ((dash--short-list-length 0)) (-uniq '(b a))) => '(b a)
+    (let ((dash--short-list-length 0)) (-uniq '(a a))) => '(a)
+    (let ((dash--short-list-length 0)) (-uniq '(a a b))) => '(a b)
+    (let ((dash--short-list-length 0)) (-uniq '(a b a))) => '(a b)
+    (let ((dash--short-list-length 0)) (-uniq '(b a a))) => '(b a)
+    (let ((dash--short-list-length 0)
+          (-compare-fn #'eq))
+      (-uniq (list (string ?a) (string ?a))))
+    => '("a" "a")
+    (let ((dash--short-list-length 0)
+          (-compare-fn #'eq)
+          (a (string ?a)))
+      (-uniq (list a a)))
+    => '("a"))
+
+  (defexamples -same-items?
+    (-same-items? '(1 2 3) '(1 2 3)) => t
+    (-same-items? '(1 1 2 3) '(3 3 2 1)) => t
+    (-same-items? '(1 2 3) '(1 2 3 4)) => nil
+    (-same-items? '((a . 1) (b . 2)) '((a . 1) (b . 2))) => t
+    (-same-items? '() '()) => t
+    (-same-items? '() '(a)) => nil
+    (-same-items? '(a) '()) => nil
+    (-same-items? '(a) '(a)) => t
+    (-same-items? '(a) '(b)) => nil
+    (-same-items? '(a) '(a a)) => t
+    (-same-items? '(b) '(a a)) => nil
+    (-same-items? '(a) '(a b)) => nil
+    (-same-items? '(a a) '(a)) => t
+    (-same-items? '(a a) '(b)) => nil
+    (-same-items? '(a a) '(a b)) => nil
+    (-same-items? '(a b) '(a)) => nil
+    (-same-items? '(a b) '(a a)) => nil
+    (-same-items? '(a a) '(a a)) => t
+    (-same-items? '(a a b) '(b b a a)) => t
+    (-same-items? '(b b a a) '(a a b)) => t
+    (let ((dash--short-list-length 0)) (-same-items? '(a) '(a))) => t
+    (let ((dash--short-list-length 0)) (-same-items? '(a) '(b))) => nil
+    (let ((dash--short-list-length 0)) (-same-items? '(a) '(a a))) => t
+    (let ((dash--short-list-length 0)) (-same-items? '(b) '(a a))) => nil
+    (let ((dash--short-list-length 0)) (-same-items? '(a) '(a b))) => nil
+    (let ((dash--short-list-length 0)) (-same-items? '(a a) '(a))) => t
+    (let ((dash--short-list-length 0)) (-same-items? '(a a) '(b))) => nil
+    (let ((dash--short-list-length 0)) (-same-items? '(a a) '(a b))) => nil
+    (let ((dash--short-list-length 0)) (-same-items? '(a b) '(a))) => nil
+    (let ((dash--short-list-length 0)) (-same-items? '(a b) '(a a))) => nil
+    (let ((dash--short-list-length 0)) (-same-items? '(a a) '(a a))) => t
+    (let ((dash--short-list-length 0)) (-same-items? '(a a b) '(b b a a))) => t
+    (let ((dash--short-list-length 0)) (-same-items? '(b b a a) '(a a b))) => 
t))
 
 (def-example-group "Other list operations"
   "Other list functions not fit to be classified elsewhere."
@@ -2137,4 +2254,47 @@ or readability."
            (equal (funcall (-compose (-prodfn f g) (-prodfn ff gg)) input3)
                   (funcall (-prodfn (-compose f ff) (-compose g gg)) 
input3)))) => t))
 
+(ert-deftest dash--member-fn ()
+  "Test `dash--member-fn'."
+  (dolist (cmp '(nil equal))
+    (let ((-compare-fn cmp))
+      (should (eq (dash--member-fn) #'member))))
+  (let ((-compare-fn #'eq))
+    (should (eq (dash--member-fn) #'memq)))
+  (let ((-compare-fn #'eql))
+    (should (eq (dash--member-fn) #'memql)))
+  (let* ((-compare-fn #'string=)
+         (member (dash--member-fn)))
+    (should-not (memq member '(member memq memql)))
+    (should-not (funcall member "foo" ()))
+    (should-not (funcall member "foo" '(bar)))
+    (should (equal (funcall member "foo" '(foo bar)) '(foo bar)))
+    (should (equal (funcall member "foo" '(bar foo)) '(foo)))))
+
+(ert-deftest dash--hash-test-fn ()
+  "Test `dash--hash-test-fn'."
+  (let ((-compare-fn nil))
+    (should (eq (dash--hash-test-fn) #'equal)))
+  (dolist (cmp '(equal eq eql))
+    (let ((-compare-fn cmp))
+      (should (eq (dash--hash-test-fn) cmp))))
+  (let ((-compare-fn #'string=))
+    (should-not (dash--hash-test-fn))))
+
+(ert-deftest dash--size+ ()
+  "Test `dash--size+'."
+  (dotimes (a 3)
+    (dotimes (b 3)
+      (should (= (dash--size+ a b) (+ a b)))))
+  (should (= (dash--size+ (- most-positive-fixnum 10) 5)
+             (- most-positive-fixnum 5)))
+  (should (= (dash--size+ (1- most-positive-fixnum) 0)
+             (1- most-positive-fixnum)))
+  (dotimes (i 2)
+    (should (= (dash--size+ (1- most-positive-fixnum) (1+ i))
+               most-positive-fixnum)))
+  (dotimes (i 3)
+    (should (= (dash--size+ most-positive-fixnum i)
+               most-positive-fixnum))))
+
 ;;; examples.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]