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))) |