master: union: use hash-tables for :test #'string=, #'=
stassats via Sbcl-commits <[email protected]> Tue, 05 May 2026 03:17:49 +0000
| Newsgroups | gmane.lisp.steel-bank.cvs |
|---|---|
| Message-ID | <[email protected]> |
The branch "master" has been updated in SBCL:
via 374a5fe50e4905fd86b73b1fe874fce51567dfb8 (commit)
from 36864b8314f0ad56c67b8631856df5468e042078 (commit)
- Log -----------------------------------------------------------------
commit 374a5fe50e4905fd86b73b1fe874fce51567dfb8
Author: Stas Boukarev <[email protected]>
Date: Tue May 5 06:13:22 2026 +0300
union: use hash-tables for :test #'string=, #'=
---
src/code/list.lisp | 60 ++++++++++++++++++++++++++++++++++--------------------
src/code/seq.lisp | 20 ------------------
2 files changed, 38 insertions(+), 42 deletions(-)
diff --git a/src/code/list.lisp b/src/code/list.lisp
index e936e7d4f..02e46d138 100644
--- a/src/code/list.lisp
+++ b/src/code/list.lisp
@@ -978,29 +978,43 @@
,@body))
+(defun string=-hash (string)
+ (sxhash (string string)))
+
+(defun string-equal-hash (string)
+ (psxhash (string string)))
+
+(declaim (inline make-hash-table-for-duplicates))
+(defun make-hash-table-for-duplicates (fun size)
+ (cond ((or (eq fun #'eq)
+ (eq fun #'eql)
+ (eq fun #'equal)
+ (eq fun #'equalp))
+ (make-hash-table :test fun :size size))
+ ((eq fun #'string=)
+ (make-hash-table :test #'string= :hash-function #'string=-hash :size size))
+ ((eq fun #'string-equal)
+ (make-hash-table :test #'string-equal :hash-function #'string-equal-hash :size size))
+ ((or (eq fun #'=)
+ (eq fun #'two-arg-=))
+ (make-hash-table :test #'two-arg-= :hash-function #'psxhash :size size))))
+
(flet ((hashing-p (notp testp test n1 n2)
(declare (index n1 n2))
;; If there is no TEST-NOT, and both lists are long enough, and the
;; test function is that of a standard hash-table, then use a hash-table.
(and (not notp)
- ;; no :TEST, or a standard hash-table test
- (or (not testp)
- (eq test #'eql)
- (eq test #'eq)
- (eq test #'equal)
- (eq test #'equalp))
(or (and (> n1 20) (> n2 20)) ; both lists are non-short
;; or one list is very long, and the other is not tiny
(and (>= n1 3) (>= n2 100))
- (and (>= n2 3) (>= n1 100)))))
- (unionize (testp test key n1 n2 set1 set2)
- (let ((table (make-hash-table :test (if testp test #'eql)
- :size (+ n1 n2))))
- (dolist (elt set1)
- (setf (gethash (apply-key key elt) table) elt))
- (dolist (elt set2)
- (setf (gethash (apply-key key elt) table) elt))
- table)))
+ (and (>= n2 3) (>= n1 100)))
+ (make-hash-table-for-duplicates (if testp test #'eql) (+ n1 n2))))
+ (unionize (table key set1 set2)
+ (dolist (elt set1)
+ (setf (gethash (apply-key key elt) table) elt))
+ (dolist (elt set2)
+ (setf (gethash (apply-key key elt) table) elt))
+ table))
;;; "If there is a duplication between list-1 and list-2, only one of the duplicate
;;; instances will be in the result. If either list-1 or list-2 has duplicate entries
@@ -1018,12 +1032,13 @@
;; (and a 1000-element list unioned with NIL should not cons a hash-table)
(cond ((null list1) (return-from union list2))
((null list2) (return-from union list1)))
- (let ((n1 (length list1))
- (n2 (length list2)))
- (if (hashing-p notp testp test n1 n2)
+ (let* ((n1 (length list1))
+ (n2 (length list2))
+ (hash-table (hashing-p notp testp test n1 n2)))
+ (if hash-table
;; "The order of elements in the result do not have to reflect the ordering
;; of list-1 or list-2 in any way."
- (loop for k being the hash-values of (unionize testp test key n1 n2 list1 list2)
+ (loop for k being the hash-values of (unionize hash-table key list1 list2)
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.
@@ -1045,9 +1060,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))))
- (if (hashing-p notp testp test n1 n2)
- (let ((table (unionize testp test key n1 n2 short long))
+ ((short long) (if (< n1 n2) (values list1 list2) (values list2 list1)))
+ (hash-table (hashing-p notp testp test n1 n2)))
+ (if hash-table
+ (let ((table (unionize hash-table key short long))
(union long)
(head long))
(maphash (lambda (k v)
diff --git a/src/code/seq.lisp b/src/code/seq.lisp
index 87c42c986..90f9b673e 100644
--- a/src/code/seq.lisp
+++ b/src/code/seq.lisp
@@ -2546,26 +2546,6 @@ many elements are copied."
;;;; REMOVE-DUPLICATES
-(defun string=-hash (string)
- (sxhash (string string)))
-
-(defun string-equal-hash (string)
- (psxhash (string string)))
-
-(declaim (inline make-hash-table-for-duplicates))
-(defun make-hash-table-for-duplicates (fun size)
- (cond ((or (eq fun #'eq)
- (eq fun #'eql)
- (eq fun #'equal)
- (eq fun #'equalp))
- (values (make-hash-table :test fun :size size) nil))
- ((eq fun #'string=)
- (make-hash-table :test #'string= :hash-function #'string=-hash :size size))
- ((eq fun #'string-equal)
- (make-hash-table :test #'string-equal :hash-function #'string-equal-hash :size size))
- ((eq fun #'=)
- (make-hash-table :test #'= :hash-function #'psxhash :size size))))
-
;;; Remove duplicates from a list. If from-end, remove the later duplicates,
;;; not the earlier ones. Thus if we check from-end we don't copy an item
;;; if we look into the already copied structure (from after :start) and see
-----------------------------------------------------------------------
hooks/post-receive
--
SBCL