... |
... |
@@ -1022,14 +1022,69 @@ |
1022
|
1022
|
(rplacd splicex (cdr x)))
|
1023
|
1023
|
(setq splicex x)))))
|
1024
|
1024
|
|
|
1025
|
+(declaim (start-block shorter-list-to-hashtable subsetp))
|
|
1026
|
+
|
|
1027
|
+(defun shorter-list-to-hashtable (list1 list2 key test test-not)
|
|
1028
|
+ ;; Find the shorter list and return the length and the shorter list
|
|
1029
|
+ (when test-not
|
|
1030
|
+ (return-from shorter-list-to-hashtable nil))
|
|
1031
|
+ (let ((hash-test (let ((test-fn (if (and (symbolp test)
|
|
1032
|
+ (fboundp test))
|
|
1033
|
+ (fdefinition test)
|
|
1034
|
+ test)))
|
|
1035
|
+ (cond ((eql test-fn #'eq) 'eq)
|
|
1036
|
+ ((eql test-fn #'eql) 'eql)
|
|
1037
|
+ ((eql test-fn #'equal) 'equal)
|
|
1038
|
+ ((eql test-fn #'equalp) 'equalp)))))
|
|
1039
|
+ (unless hash-test
|
|
1040
|
+ (return-from shorter-list-to-hashtable nil))
|
|
1041
|
+ (multiple-value-bind (min-length shorter-list)
|
|
1042
|
+ (do ((len 0 (1+ len))
|
|
1043
|
+ (lst1 list1 (cdr lst1))
|
|
1044
|
+ (lst2 list2 (cdr lst2)))
|
|
1045
|
+ ((or (null lst1) (null lst2))
|
|
1046
|
+ (values len (if (null lst1) list1 list2))))
|
|
1047
|
+ (when (< min-length kernel::*min-list-length-for-subsetp-hashtable*)
|
|
1048
|
+ (return-from shorter-list-to-hashtable nil))
|
|
1049
|
+ (let ((hashtable (make-hash-table :test hash-test :size min-length)))
|
|
1050
|
+ (dolist (item shorter-list)
|
|
1051
|
+ (setf (gethash (apply-key key item) hashtable) item))
|
|
1052
|
+ (values hashtable shorter-list)))))
|
|
1053
|
+
|
1025
|
1054
|
(defun subsetp (list1 list2 &key key (test #'eql testp) (test-not nil notp))
|
1026
|
1055
|
"Returns T if every element in list1 is also in list2."
|
1027
|
1056
|
(declare (inline member))
|
1028
|
|
- (dolist (elt list1)
|
1029
|
|
- (unless (with-set-keys (member (apply-key key elt) list2))
|
1030
|
|
- (return-from subsetp nil)))
|
1031
|
|
- T)
|
|
1057
|
+ (when (and testp notp)
|
|
1058
|
+ (error "Test and test-not both supplied."))
|
|
1059
|
+
|
|
1060
|
+ ;; SUBSETP is used early in TYPE-INIT where hash tables aren't
|
|
1061
|
+ ;; available yet, so we can't use hashtables then. LISPINIT will
|
|
1062
|
+ ;; take care to disable this for the kernel.core. SAVE will set
|
|
1063
|
+ ;; this to true when it's safe to use hash tables for SUBSETP.
|
|
1064
|
+ (multiple-value-bind (hashtable shorter-list)
|
|
1065
|
+ (when t
|
|
1066
|
+ (shorter-list-to-hashtable list1 list2 key test test-not))
|
|
1067
|
+ (cond (hashtable
|
|
1068
|
+ (cond ((eq shorter-list list1)
|
|
1069
|
+ ;; Remove any item from list2 from the hashtable containing list1.
|
|
1070
|
+ (dolist (item list2)
|
|
1071
|
+ (remhash (apply-key key item) hashtable))
|
|
1072
|
+ ;; If the hash table is now empty, then every
|
|
1073
|
+ ;; element in list1 appeared in list2, so list1 is a
|
|
1074
|
+ ;; subset of list2.
|
|
1075
|
+ (zerop (hash-table-count hashtable)))
|
|
1076
|
+ ((eq shorter-list list2)
|
|
1077
|
+ (dolist (item list1)
|
|
1078
|
+ (unless (nth-value 1 (gethash (apply-key key item) hashtable))
|
|
1079
|
+ (return-from subsetp nil)))
|
|
1080
|
+ t)))
|
|
1081
|
+ (t
|
|
1082
|
+ (dolist (item list1)
|
|
1083
|
+ (unless (with-set-keys (member (apply-key key item) list2))
|
|
1084
|
+ (return-from subsetp nil)))
|
|
1085
|
+ T))))
|
1032
|
1086
|
|
|
1087
|
+(declaim (end-block))
|
1033
|
1088
|
|
1034
|
1089
|
|
1035
|
1090
|
;;; Functions that operate on association lists
|