Raymond Toy pushed to branch master at cmucl / cmucl
Commits:
-
0b497d8c
by Raymond Toy at 2023-08-21T20:15:07+00:00
-
bfae1626
by Raymond Toy at 2023-08-21T20:15:21+00:00
4 changed files:
Changes:
| ... | ... | @@ -989,14 +989,69 @@ |
| 989 | 989 | (rplacd splicex (cdr x)))
|
| 990 | 990 | (setq splicex x)))))
|
| 991 | 991 | |
| 992 | +(declaim (start-block shorter-list-to-hashtable subsetp))
|
|
| 993 | + |
|
| 994 | +(defun shorter-list-to-hashtable (list1 list2 key test test-not)
|
|
| 995 | + ;; Find the shorter list and return the length and the shorter list
|
|
| 996 | + (when test-not
|
|
| 997 | + (return-from shorter-list-to-hashtable nil))
|
|
| 998 | + (let ((hash-test (let ((test-fn (if (and (symbolp test)
|
|
| 999 | + (fboundp test))
|
|
| 1000 | + (fdefinition test)
|
|
| 1001 | + test)))
|
|
| 1002 | + (cond ((eql test-fn #'eq) 'eq)
|
|
| 1003 | + ((eql test-fn #'eql) 'eql)
|
|
| 1004 | + ((eql test-fn #'equal) 'equal)
|
|
| 1005 | + ((eql test-fn #'equalp) 'equalp)))))
|
|
| 1006 | + (unless hash-test
|
|
| 1007 | + (return-from shorter-list-to-hashtable nil))
|
|
| 1008 | + (multiple-value-bind (min-length shorter-list)
|
|
| 1009 | + (do ((len 0 (1+ len))
|
|
| 1010 | + (lst1 list1 (cdr lst1))
|
|
| 1011 | + (lst2 list2 (cdr lst2)))
|
|
| 1012 | + ((or (null lst1) (null lst2))
|
|
| 1013 | + (values len (if (null lst1) list1 list2))))
|
|
| 1014 | + (when (< min-length kernel::*min-list-length-for-subsetp-hashtable*)
|
|
| 1015 | + (return-from shorter-list-to-hashtable nil))
|
|
| 1016 | + (let ((hashtable (make-hash-table :test hash-test :size min-length)))
|
|
| 1017 | + (dolist (item shorter-list)
|
|
| 1018 | + (setf (gethash (apply-key key item) hashtable) item))
|
|
| 1019 | + (values hashtable shorter-list)))))
|
|
| 1020 | +
|
|
| 992 | 1021 | (defun subsetp (list1 list2 &key key (test #'eql testp) (test-not nil notp))
|
| 993 | 1022 | "Returns T if every element in list1 is also in list2."
|
| 994 | 1023 | (declare (inline member))
|
| 995 | - (dolist (elt list1)
|
|
| 996 | - (unless (with-set-keys (member (apply-key key elt) list2))
|
|
| 997 | - (return-from subsetp nil)))
|
|
| 998 | - T)
|
|
| 1024 | + (when (and testp notp)
|
|
| 1025 | + (error "Test and test-not both supplied."))
|
|
| 1026 | + |
|
| 1027 | + ;; SUBSETP is used early in TYPE-INIT where hash tables aren't
|
|
| 1028 | + ;; available yet, so we can't use hashtables then. LISPINIT will
|
|
| 1029 | + ;; take care to disable this for the kernel.core. SAVE will set
|
|
| 1030 | + ;; this to true when it's safe to use hash tables for SUBSETP.
|
|
| 1031 | + (multiple-value-bind (hashtable shorter-list)
|
|
| 1032 | + (when t
|
|
| 1033 | + (shorter-list-to-hashtable list1 list2 key test test-not))
|
|
| 1034 | + (cond (hashtable
|
|
| 1035 | + (cond ((eq shorter-list list1)
|
|
| 1036 | + ;; Remove any item from list2 from the hashtable containing list1.
|
|
| 1037 | + (dolist (item list2)
|
|
| 1038 | + (remhash (apply-key key item) hashtable))
|
|
| 1039 | + ;; If the hash table is now empty, then every
|
|
| 1040 | + ;; element in list1 appeared in list2, so list1 is a
|
|
| 1041 | + ;; subset of list2.
|
|
| 1042 | + (zerop (hash-table-count hashtable)))
|
|
| 1043 | + ((eq shorter-list list2)
|
|
| 1044 | + (dolist (item list1)
|
|
| 1045 | + (unless (nth-value 1 (gethash (apply-key key item) hashtable))
|
|
| 1046 | + (return-from subsetp nil)))
|
|
| 1047 | + t)))
|
|
| 1048 | + (t
|
|
| 1049 | + (dolist (item list1)
|
|
| 1050 | + (unless (with-set-keys (member (apply-key key item) list2))
|
|
| 1051 | + (return-from subsetp nil)))
|
|
| 1052 | + T))))
|
|
| 999 | 1053 | |
| 1054 | +(declaim (end-block))
|
|
| 1000 | 1055 | |
| 1001 | 1056 | |
| 1002 | 1057 | ;;; Functions that operate on association lists
|
| ... | ... | @@ -377,6 +377,14 @@ |
| 377 | 377 | (cold-load-init (setq *use-implementation-types* t))
|
| 378 | 378 | (declaim (type boolean *use-implementation-types*))
|
| 379 | 379 | |
| 380 | +(defvar *min-list-length-for-subsetp-hashtable* 150
|
|
| 381 | + "The minimum length of either list argument for subsetp where a
|
|
| 382 | + hashtable is used to speed up processing instead of using a basic
|
|
| 383 | + list implementation. This value was determined by experimentation.")
|
|
| 384 | + |
|
| 385 | +(cold-load-init (setq *min-list-length-for-subsetp-hashtable* 150))
|
|
| 386 | +(declaim (type fixnum *min-list-length-for-subsetp-hashtable*))
|
|
| 387 | + |
|
| 380 | 388 | ;;; DELEGATE-COMPLEX-{SUBTYPEP-ARG2,INTERSECTION} -- Interface
|
| 381 | 389 | ;;;
|
| 382 | 390 | ;;; These functions are used as method for types which need a complex
|
| ... | ... | @@ -1155,6 +1155,13 @@ msgid "" |
| 1155 | 1155 | " affects array types."
|
| 1156 | 1156 | msgstr ""
|
| 1157 | 1157 | |
| 1158 | +#: src/code/type.lisp
|
|
| 1159 | +msgid ""
|
|
| 1160 | +"The minimum length of either list argument for subsetp where a\n"
|
|
| 1161 | +" hashtable is used to speed up processing instead of using a basic\n"
|
|
| 1162 | +" list implementation. This value was determined by experimentation."
|
|
| 1163 | +msgstr ""
|
|
| 1164 | + |
|
| 1158 | 1165 | #: src/code/type.lisp
|
| 1159 | 1166 | msgid "Subtypep is illegal on this type:~% ~S"
|
| 1160 | 1167 | msgstr ""
|
| ... | ... | @@ -172,3 +172,54 @@ |
| 172 | 172 | '(3 4)
|
| 173 | 173 | :test 'eql
|
| 174 | 174 | :test-not 'eql)))
|
| 175 | + |
|
| 176 | + |
|
| 177 | +(define-test subsetp.hash-eq
|
|
| 178 | + (:tag :issues)
|
|
| 179 | + (let ((lisp::*min-list-length-for-hashtable* 2))
|
|
| 180 | + (assert-true
|
|
| 181 | + (subsetp '(a b c a) '(a a d d c b) :test 'eq))
|
|
| 182 | + (assert-true
|
|
| 183 | + (subsetp '(a b c a b c a b c) '(a a d d c b) :test 'eq))
|
|
| 184 | + (assert-false
|
|
| 185 | + (subsetp '(a b c a z) '(a a d d c b) :test 'eq))
|
|
| 186 | + (assert-false
|
|
| 187 | + (subsetp '(a b c a b cz) '(a a d d c b) :test 'eq))))
|
|
| 188 | + |
|
| 189 | +(define-test subsetp.hash-eql
|
|
| 190 | + (:tag :issues)
|
|
| 191 | + (let ((lisp::*min-list-length-for-hashtable* 2))
|
|
| 192 | + (assert-true
|
|
| 193 | + (subsetp '(a b c a) '(a a d d c b) :test 'eql))
|
|
| 194 | + (assert-false
|
|
| 195 | + (subsetp '(a b c a z) '(a a d d c b) :test 'eql))))
|
|
| 196 | + |
|
| 197 | +(define-test subsetp.hash-equal
|
|
| 198 | + (:tag :issues)
|
|
| 199 | + (let ((lisp::*min-list-length-for-hashtable* 2))
|
|
| 200 | + (assert-true
|
|
| 201 | + (subsetp '("a" "b" "c" "a") '("a" "a" "d" "d" "c" "b") :test 'equal))
|
|
| 202 | + (assert-false
|
|
| 203 | + (subsetp '("a" "b" "c" "a" "z") '("a" "a" "d" "d" "c" "b") :test 'equal))))
|
|
| 204 | + |
|
| 205 | +(define-test subsetp.hash-equalp
|
|
| 206 | + (:tag :issues)
|
|
| 207 | + (let ((lisp::*min-list-length-for-hashtable* 2))
|
|
| 208 | + (assert-true
|
|
| 209 | + (subsetp '("a" "b" "C" "A") '("a" "a" "d" "d" "c" "b") :test 'equalp))
|
|
| 210 | + (assert-false
|
|
| 211 | + (subsetp '("a" "b" "C" "A" "z") '("a" "a" "d" "d" "c" "b") :test 'equalp))))
|
|
| 212 | + |
|
| 213 | +(define-test subsetp.hash-eql-with-key
|
|
| 214 | + (:tag :issues)
|
|
| 215 | + (assert-true (subsetp '((1 "a") (2 "b") (3 "c"))
|
|
| 216 | + '((3 "c") (3 "c") (2 "b") (1 "a"))
|
|
| 217 | + :test 'eql
|
|
| 218 | + :key #'first)))
|
|
| 219 | + |
|
| 220 | +(define-test subsetp.test-and-test-not
|
|
| 221 | + (assert-error 'simple-error
|
|
| 222 | + (subsetp '(1 2)
|
|
| 223 | + '(3 4)
|
|
| 224 | + :test 'eql
|
|
| 225 | + :test-not 'equal))) |