Raymond Toy pushed to branch issue-240-clean-up-hashtable-for-sets-impl at cmucl / cmucl

Commits:

1 changed file:

Changes:

  • src/code/list.lisp
    ... ... @@ -753,17 +753,11 @@
    753 753
                           union intersection set-difference
    
    754 754
                           nunion nintersection nset-difference))
    
    755 755
     
    
    756
    -;; Main code to process a set function.  INIT-RES initializes the
    
    757
    -;; value of RES, which holds the result of the set function.
    
    758
    -;; TEST-FORM is a form that tests whether to add the item from LIST1
    
    759
    -;; to RES.
    
    760
    -(defmacro process-set-body (list1 init-res membership-test test-form)
    
    761
    -  `(let ((res ,init-res))
    
    762
    -     (dolist (item ,list1)
    
    763
    -       (,membership-test ,test-form
    
    764
    -         (push item res)))
    
    765
    -     res))
    
    766
    -
    
    756
    +;; Handle a non-destructive set operation.  LIST1 and LIST2 are the
    
    757
    +;; two arguments to the set function.  INITIAL-RESULT is the value
    
    758
    +;; used to initialize the result list.  IS specifies whether the test
    
    759
    +;; (or test-not) function implies an element of LIST1 should be
    
    760
    +;; included in the result.
    
    767 761
     (defmacro do-set-operation (list1 list2 &key initial-result is)
    
    768 762
       (let ((membership-test (ecase is
    
    769 763
                                (:element-of-set
    
    ... ... @@ -771,11 +765,19 @@
    771 765
                                (:not-element-of-set
    
    772 766
                                 'unless))))
    
    773 767
         `(let ((hashtable (list-to-hashtable ,list2 key test test-not)))
    
    774
    -       (if hashtable
    
    775
    -           (process-set-body ,list1 ,initial-result ,membership-test
    
    768
    +       (macrolet
    
    769
    +           ((process-set-op (list1 init-res member-form test-form)
    
    770
    +              `(let ((res ,init-res))
    
    771
    +                 (dolist (item ,list1)
    
    772
    +                   (,member-form ,test-form
    
    773
    +                                 (push item res)))
    
    774
    +                 res)))
    
    775
    +
    
    776
    +         (if hashtable
    
    777
    +             (process-set-op ,list1 ,initial-result ,membership-test
    
    776 778
                                  (nth-value 1 (gethash (apply-key key item) hashtable)))
    
    777
    -           (process-set-body ,list1 ,initial-result ,membership-test
    
    778
    -                             (with-set-keys (member (apply-key key item) ,list2)))))))
    
    779
    +             (process-set-op ,list1 ,initial-result ,membership-test
    
    780
    +                             (with-set-keys (member (apply-key key item) list2))))))))
    
    779 781
     
    
    780 782
     ;; Convert a list to a hashtable.  The hashtable does not handle
    
    781 783
     ;; duplicated values in the list.  Returns the hashtable.
    
    ... ... @@ -854,14 +856,14 @@
    854 856
     ;;; the result list.  INVERT-P is T if the result of the TEST-FORM
    
    855 857
     ;;; should be inverted.  TEST-FORM is the form used for determining
    
    856 858
     ;;; how to update the result.
    
    857
    -(defmacro nprocess-set-body (init-res invert-p test-form)
    
    859
    +(defmacro nprocess-set-body (list1 init-res is-member-p test-form)
    
    858 860
       `(let ((res ,init-res)
    
    859
    -         (list1 list1))
    
    861
    +         (list1 ,list1))
    
    860 862
          (do ()
    
    861 863
              ((endp list1))
    
    862
    -       (if ,(if invert-p
    
    863
    -                `(not ,test-form)
    
    864
    -                test-form)
    
    864
    +       (if ,(if is-member-p
    
    865
    +                test-form
    
    866
    +                `(not ,test-form))
    
    865 867
                (steve-splice list1 res)
    
    866 868
                (setq list1 (cdr list1))))
    
    867 869
          res))
    
    ... ... @@ -870,13 +872,30 @@
    870 872
     ;; initializes the value of the result list.  INVERT-P indicates
    
    871 873
     ;; whether to invert the test-form used to determine how the result
    
    872 874
     ;; should be updated.
    
    873
    -(defmacro nprocess-set (init-res invert-p)
    
    874
    -  `(let ((hashtable (list-to-hashtable list2 key test test-not)))
    
    875
    -     (if hashtable
    
    876
    -         (nprocess-set-body ,init-res ,invert-p
    
    877
    -                            (nth-value 1 (gethash (apply-key key (car list1)) hashtable)))
    
    878
    -         (nprocess-set-body ,init-res ,invert-p
    
    879
    -                            (with-set-keys (member (apply-key key (car list1)) list2))))))
    
    875
    +(defmacro do-destructive-set-operation (list1 list2 &key initial-result is)
    
    876
    +  (let ((is-member-p (ecase is
    
    877
    +                       (:element-of-set
    
    878
    +                        t)
    
    879
    +                       (:not-element-of-set
    
    880
    +                        nil))))
    
    881
    +    `(let ((hashtable (list-to-hashtable ,list2 key test test-not)))
    
    882
    +       (macrolet
    
    883
    +           ((process-set-op (list1 init-res is-member-p test-form)
    
    884
    +              `(let ((res ,init-res)
    
    885
    +                     (list1 ,list1))
    
    886
    +                 (do ()
    
    887
    +                     ((endp list1))
    
    888
    +                   (if ,(if is-member-p
    
    889
    +                            test-form
    
    890
    +                            `(not ,test-form))
    
    891
    +                       (steve-splice list1 res)
    
    892
    +                       (setq list1 (cdr list1))))
    
    893
    +                 res)))
    
    894
    +         (if hashtable
    
    895
    +             (process-set-op ,list1 ,initial-result ,is-member-p
    
    896
    +                             (nth-value 1 (gethash (apply-key key (car ,list1)) hashtable)))
    
    897
    +             (process-set-op ,list1 ,initial-result ,is-member-p
    
    898
    +                             (with-set-keys (member (apply-key key (car ,list1)) list2))))))))
    
    880 899
     
    
    881 900
     
    
    882 901
     (defun nunion (list1 list2 &key key (test #'eql testp) (test-not nil notp))
    
    ... ... @@ -885,7 +904,7 @@
    885 904
       (if (and testp notp)
    
    886 905
           (error "Test and test-not both supplied."))
    
    887 906
     
    
    888
    -  (nprocess-set list2 t))
    
    907
    +  (do-destructive-set-operation list1 list2 :initial-result list2 :is :not-element-of-set))
    
    889 908
       
    
    890 909
     (defun nintersection (list1 list2 &key key
    
    891 910
     			    (test #'eql testp) (test-not nil notp))
    
    ... ... @@ -894,7 +913,7 @@
    894 913
       (if (and testp notp)
    
    895 914
           (error "Test and test-not both supplied."))
    
    896 915
     
    
    897
    -  (nprocess-set nil nil))
    
    916
    +  (do-destructive-set-operation list1 list2 :initial-result nil :is :element-of-set))
    
    898 917
     
    
    899 918
     (defun nset-difference (list1 list2 &key key
    
    900 919
     			      (test #'eql testp) (test-not nil notp))
    
    ... ... @@ -903,7 +922,7 @@
    903 922
       (if (and testp notp)
    
    904 923
           (error "Test and test-not both supplied."))
    
    905 924
     
    
    906
    -  (nprocess-set nil t))
    
    925
    +  (do-destructive-set-operation list1 list2 :initial-result nil :is :not-element-of-set))
    
    907 926
     
    
    908 927
     (declaim (end-block))
    
    909 928