master: union: preserve the argument order passed to :test
stassats via Sbcl-commits <[email protected]> Tue, 05 May 2026 05:43:39 +0000
| Newsgroups | gmane.lisp.steel-bank.cvs |
|---|---|
| Message-ID | <[email protected]> |
The branch "master" has been updated in SBCL:
via c7b985482ab1db2efa403a846073b3759dd61adb (commit)
from 374a5fe50e4905fd86b73b1fe874fce51567dfb8 (commit)
- Log -----------------------------------------------------------------
commit c7b985482ab1db2efa403a846073b3759dd61adb
Author: Stas Boukarev <[email protected]>
Date: Tue May 5 07:33:09 2026 +0300
union: preserve the argument order passed to :test
Independent of the lengths of the input lists.
---
src/code/list.lisp | 41 ++++++++++++++++++++++++++++-------------
src/compiler/ir1util.lisp | 11 +++++++----
tests/list.pure.lisp | 16 ++++++++++++++++
3 files changed, 51 insertions(+), 17 deletions(-)
diff --git a/src/code/list.lisp b/src/code/list.lisp
index 02e46d138..5d4fb85e9 100644
--- a/src/code/list.lisp
+++ b/src/code/list.lisp
@@ -1042,12 +1042,18 @@
collect k)
;; Start with the initial result being the shorter of the inputs.
;; Search for each element of the longer in the shorter, adding the missing ones.
- (multiple-value-bind (short long)
- (if (< n1 n2) (values list1 list2) (values list2 list1))
- (let ((result short))
- (dolist (elt long result)
- (unless (funcall member-test elt short key test)
- (push elt result)))))))))
+ (flet ((swapped-test (x y)
+ (funcall test y x)))
+ (declare (dynamic-extent #'swapped-test))
+ (multiple-value-bind (short long test)
+ (if (< n1 n2)
+ (values list1 list2 (and test
+ #'swapped-test))
+ (values list2 list1 test))
+ (let ((result short))
+ (dolist (elt long result)
+ (unless (funcall member-test elt short key test)
+ (push elt result))))))))))
(defun nunion (list1 list2 &key key (test nil testp) (test-not nil notp))
"Destructively return the union of LIST1 and LIST2."
@@ -1060,7 +1066,10 @@
((null list2) (return-from nunion list1)))
(binding* ((n1 (length list1))
(n2 (length list2))
- ((short long) (if (< n1 n2) (values list1 list2) (values list2 list1)))
+ ((short long swap)
+ (if (< n1 n2)
+ (values list1 list2 t)
+ (values list2 list1 nil)))
(hash-table (hashing-p notp testp test n1 n2)))
(if hash-table
(let ((table (unionize hash-table key short long))
@@ -1074,12 +1083,18 @@
(push v union))) ; easier than re-using cons cells of SHORT
table)
union)
- (do ((orig short)
- (elt (car long) (car long)))
- ((endp long) short)
- (if (funcall member-test elt orig key test)
- (pop long)
- (shiftf long (cdr long) short long))))))))
+ (flet ((swapped-test (x y)
+ (funcall (truly-the function test) y x)))
+ (declare (dynamic-extent #'swapped-test))
+ (let ((test (if swap
+ (and test #'swapped-test)
+ test)))
+ (do ((orig short)
+ (elt (car long) (car long)))
+ ((endp long) short)
+ (if (funcall member-test elt orig key test)
+ (pop long)
+ (shiftf long (cdr long) short long))))))))))
(defun intersection (list1 list2
&key key (test nil testp) (test-not nil notp))
diff --git a/src/compiler/ir1util.lisp b/src/compiler/ir1util.lisp
index d8faabc22..0c43c2c9c 100644
--- a/src/compiler/ir1util.lisp
+++ b/src/compiler/ir1util.lisp
@@ -1547,10 +1547,13 @@
(lvar-dest lvar))))
(when (and (combination-p combination)
(eq (combination-fun combination) lvar))
- (loop for v in vars
- for arg in (combination-args combination)
- when (eq v var)
- return arg))))))
+ (let ((args (combination-args combination)))
+ (when (functional-kind-eq fun external toplevel-xep)
+ (pop args)) ;; arg-count
+ (loop for v in vars
+ for arg in args
+ when (eq v var)
+ return arg)))))))
;;; Return the Top Level Form number of PATH, i.e. the ordinal number
;;; of its original source's top level form in its compilation unit.
diff --git a/tests/list.pure.lisp b/tests/list.pure.lisp
index d30d37bb2..05727fcd2 100644
--- a/tests/list.pure.lisp
+++ b/tests/list.pure.lisp
@@ -466,3 +466,19 @@
(compile 'try-dx-acons)
(with-test (:name :compiled-acons)
(assert (try-dx-acons 1 2 3 '((1 . 2) . 3))))
+
+(with-test (:name :sets-test-order)
+ (flet ((test-fun (a b)
+ (declare (integer a) (float b))
+ (= a b)))
+ (mapc (lambda (fun)
+ (funcall (opaque-identity fun)
+ (list 1 2 3) (list 1.0 2.0 3.0 4.0)
+ :test #'test-fun)
+ (funcall (opaque-identity fun)
+ (list 1 2 3 4) (list 1.0 2.0 3.0)
+ :test #'test-fun))
+ '(union nunion intersection nintersection
+ set-difference nset-difference
+ set-exclusive-or nset-exclusive-or
+ subsetp))))
-----------------------------------------------------------------------
hooks/post-receive
--
SBCL