Raymond Toy pushed to branch master at cmucl / cmucl
Commits:
-
87e54e03
by Raymond Toy at 2023-08-25T14:15:52+00:00
-
4542d55e
by Raymond Toy at 2023-08-25T14:16:04+00:00
2 changed files:
Changes:
| ... | ... | @@ -749,7 +749,9 @@ |
| 749 | 749 | (defparameter *min-list-length-for-hashtable*
|
| 750 | 750 | 15)
|
| 751 | 751 | |
| 752 | -(declaim (start-block list-to-hashtable union intersection set-difference))
|
|
| 752 | +(declaim (start-block list-to-hashtable
|
|
| 753 | + union intersection set-difference
|
|
| 754 | + nunion nintersection nset-difference))
|
|
| 753 | 755 | |
| 754 | 756 | ;; Main code to process a set function. INIT-RES initializes the
|
| 755 | 757 | ;; value of RES, which holds the result of the set function.
|
| ... | ... | @@ -836,8 +838,6 @@ |
| 836 | 838 | (process-set nil t))
|
| 837 | 839 | |
| 838 | 840 | |
| 839 | -(declaim (end-block))
|
|
| 840 | - |
|
| 841 | 841 | ;;; Destination and source are setf-able and many-evaluable. Sets the source
|
| 842 | 842 | ;;; to the cdr, and "conses" the 1st elt of source to destination.
|
| 843 | 843 | ;;;
|
| ... | ... | @@ -847,19 +847,42 @@ |
| 847 | 847 | (cdr temp) ,destination
|
| 848 | 848 | ,destination temp)))
|
| 849 | 849 | |
| 850 | +;;; Main body for destructive set operations. INIT-RES initializes
|
|
| 851 | +;;; the result list. INVERT-P is T if the result of the TEST-FORM
|
|
| 852 | +;;; should be inverted. TEST-FORM is the form used for determining
|
|
| 853 | +;;; how to update the result.
|
|
| 854 | +(defmacro nprocess-set-body (init-res invert-p test-form)
|
|
| 855 | + `(let ((res ,init-res)
|
|
| 856 | + (list1 list1))
|
|
| 857 | + (do ()
|
|
| 858 | + ((endp list1))
|
|
| 859 | + (if ,(if invert-p
|
|
| 860 | + `(not ,test-form)
|
|
| 861 | + test-form)
|
|
| 862 | + (steve-splice list1 res)
|
|
| 863 | + (setq list1 (cdr list1))))
|
|
| 864 | + res))
|
|
| 865 | + |
|
| 866 | +;; Implementation of the destructive set operations. INIT-RES
|
|
| 867 | +;; initializes the value of the result list. INVERT-P indicates
|
|
| 868 | +;; whether to invert the test-form used to determine how the result
|
|
| 869 | +;; should be updated.
|
|
| 870 | +(defmacro nprocess-set (init-res invert-p)
|
|
| 871 | + `(let ((hashtable (list-to-hashtable list2 key test test-not)))
|
|
| 872 | + (if hashtable
|
|
| 873 | + (nprocess-set-body ,init-res ,invert-p
|
|
| 874 | + (nth-value 1 (gethash (apply-key key (car list1)) hashtable)))
|
|
| 875 | + (nprocess-set-body ,init-res ,invert-p
|
|
| 876 | + (with-set-keys (member (apply-key key (car list1)) list2))))))
|
|
| 877 | + |
|
| 878 | + |
|
| 850 | 879 | (defun nunion (list1 list2 &key key (test #'eql testp) (test-not nil notp))
|
| 851 | 880 | "Destructively returns the union list1 and list2."
|
| 852 | 881 | (declare (inline member))
|
| 853 | 882 | (if (and testp notp)
|
| 854 | 883 | (error "Test and test-not both supplied."))
|
| 855 | - (let ((res list2)
|
|
| 856 | - (list1 list1))
|
|
| 857 | - (do ()
|
|
| 858 | - ((endp list1))
|
|
| 859 | - (if (not (with-set-keys (member (apply-key key (car list1)) list2)))
|
|
| 860 | - (steve-splice list1 res)
|
|
| 861 | - (setf list1 (cdr list1))))
|
|
| 862 | - res))
|
|
| 884 | + |
|
| 885 | + (nprocess-set list2 t))
|
|
| 863 | 886 |
|
| 864 | 887 | (defun nintersection (list1 list2 &key key
|
| 865 | 888 | (test #'eql testp) (test-not nil notp))
|
| ... | ... | @@ -867,13 +890,8 @@ |
| 867 | 890 | (declare (inline member))
|
| 868 | 891 | (if (and testp notp)
|
| 869 | 892 | (error "Test and test-not both supplied."))
|
| 870 | - (let ((res nil)
|
|
| 871 | - (list1 list1))
|
|
| 872 | - (do () ((endp list1))
|
|
| 873 | - (if (with-set-keys (member (apply-key key (car list1)) list2))
|
|
| 874 | - (steve-splice list1 res)
|
|
| 875 | - (setq list1 (Cdr list1))))
|
|
| 876 | - res))
|
|
| 893 | + |
|
| 894 | + (nprocess-set nil nil))
|
|
| 877 | 895 | |
| 878 | 896 | (defun nset-difference (list1 list2 &key key
|
| 879 | 897 | (test #'eql testp) (test-not nil notp))
|
| ... | ... | @@ -881,14 +899,10 @@ |
| 881 | 899 | (declare (inline member))
|
| 882 | 900 | (if (and testp notp)
|
| 883 | 901 | (error "Test and test-not both supplied."))
|
| 884 | - (let ((res nil)
|
|
| 885 | - (list1 list1))
|
|
| 886 | - (do () ((endp list1))
|
|
| 887 | - (if (not (with-set-keys (member (apply-key key (car list1)) list2)))
|
|
| 888 | - (steve-splice list1 res)
|
|
| 889 | - (setq list1 (cdr list1))))
|
|
| 890 | - res))
|
|
| 891 | 902 | |
| 903 | + (nprocess-set nil t))
|
|
| 904 | + |
|
| 905 | +(declaim (end-block))
|
|
| 892 | 906 | |
| 893 | 907 | (defun set-exclusive-or (list1 list2 &key key
|
| 894 | 908 | (test #'eql testp) (test-not nil notp))
|
| ... | ... | @@ -88,8 +88,37 @@ |
| 88 | 88 | :test 'eql
|
| 89 | 89 | :test-not 'eql)))
|
| 90 | 90 | |
| 91 | -
|
|
| 92 | 91 | |
| 92 | +(define-test nset-diff.1
|
|
| 93 | + (:tag :issues)
|
|
| 94 | + ;; From CLHS
|
|
| 95 | + (flet
|
|
| 96 | + ((test1 (min-length-limit)
|
|
| 97 | + (let ((lisp::*min-list-length-for-hashtable* min-length-limit)
|
|
| 98 | + (lst1 (list "A" "b" "C" "d"))
|
|
| 99 | + (lst2 (list "a" "B" "C" "d")))
|
|
| 100 | + (assert-equal '("b" "A")
|
|
| 101 | + (nset-difference lst1 lst2 :test 'equal))
|
|
| 102 | + ;; This isn't specified by the CLHS, but it is what we do.
|
|
| 103 | + (assert-equal '("A") lst1))))
|
|
| 104 | + (test1 100)
|
|
| 105 | + (test1 1)))
|
|
| 106 | + |
|
| 107 | +(define-test nset-diff.key
|
|
| 108 | + (:tag :issues)
|
|
| 109 | + (flet
|
|
| 110 | + ((test (min-length-limit)
|
|
| 111 | + ;; From CLHS
|
|
| 112 | + (let ((lisp::*min-list-length-for-hashtable* min-length-limit)
|
|
| 113 | + (lst1 (list '("a" . "b") '("c" . "d") '("e" . "f")))
|
|
| 114 | + (lst2 (list '("c" . "a") '("e" . "b") '("d" . "a"))))
|
|
| 115 | + (assert-equal '(("e" . "f") ("c" . "d"))
|
|
| 116 | + (nset-difference lst1 lst2 :test 'equal :key #'cdr))
|
|
| 117 | + ;; This isn't specified by the CLHS, but it is what we do.
|
|
| 118 | + (assert-equal '(("a" . "b") ("c" . "d")) lst1))))
|
|
| 119 | + (test 100)
|
|
| 120 | + (test 1)))
|
|
| 121 | +
|
|
| 93 | 122 | (define-test union.hash-eql
|
| 94 | 123 | (:tag :issues)
|
| 95 | 124 | ;; For union to use hashtables by making the threshold
|
| ... | ... | @@ -173,6 +202,33 @@ |
| 173 | 202 | :test 'eql
|
| 174 | 203 | :test-not 'eql)))
|
| 175 | 204 | |
| 205 | +(define-test nunion.1
|
|
| 206 | + (:tag :issues)
|
|
| 207 | + (flet
|
|
| 208 | + ((test (min-list-length)
|
|
| 209 | + (let ((lisp::*min-list-length-for-hashtable* min-list-length)
|
|
| 210 | + (lst1 (list 1 2 '(1 2) "a" "b"))
|
|
| 211 | + (lst2 (list 2 3 '(2 3) "B" "C")))
|
|
| 212 | + (assert-equal '("b" "a" (1 2) 1 2 3 (2 3) "B" "C")
|
|
| 213 | + (nunion lst1 lst2))
|
|
| 214 | + (assert-equal '(1 2 3 (2 3) "B" "C")
|
|
| 215 | + lst1))))
|
|
| 216 | + (test 100)
|
|
| 217 | + (test 1)))
|
|
| 218 | + |
|
| 219 | +(define-test nintersection.1
|
|
| 220 | + (:tag :issues)
|
|
| 221 | + (flet
|
|
| 222 | + ((test (min-list-length)
|
|
| 223 | + (let ((lisp::*min-list-length-for-hashtable* min-list-length)
|
|
| 224 | + (lst1 (list 1 1 2 3 4 'a 'b 'c "A" "B" "C" "d"))
|
|
| 225 | + (lst2 (list 1 4 5 'b 'c 'd "a" "B" "c" "D")))
|
|
| 226 | + (assert-equal '(c b 4 1 1)
|
|
| 227 | + (nintersection lst1 lst2))
|
|
| 228 | + (assert-equal '(1) lst1))))
|
|
| 229 | + (test 100)
|
|
| 230 | + (test 1)))
|
|
| 231 | + |
|
| 176 | 232 | |
| 177 | 233 | (define-test subsetp.hash-eq
|
| 178 | 234 | (:tag :issues)
|