I think there are some improvements that can be made regarding your current approach. But first, let the basic cartisian-product be implemented. Then we can adapt it to accept the ignore list. This is easy enough using for and some recursion:
(defn cartesian-product [colls] (if (empty? colls) (list ()) (for [e (first colls) sub-product (cartesian-product (rest colls))] (cons e sub-product)))) ;; Quick test run (cartesian-product [[:a :b :c] [:d :e] [:f]]) => ((:a :d :f) (:a :e :f) (:b :d :f) (:b :e :f) (:c :d :f) (:c :e :f))
Good. And since we use for , we have the advantage of laziness. If you need your result as something other than a sequence of sequences, it's easy enough to convert it to something else.
Now the tricky part is implementing ignore sets. According to your description, your current approach is to remove elements from A_i, if you add them to the term that you are building, this term will become a superset of any of the ignore sets. As your code shows, this is not only somewhat inefficient (for example, superset? Is the worst linear time by the size of its first parameter), but it also makes the code more complex than necessary.
So let's take a different approach. Instead of deleting elements from A_i, we will remove any elements that we add to the term from ignore sets. Then we can trim the term if any of the ignored sets is empty. As a bonus, all that is required is a few changes to our previous implementation of cartesian-product :
(defn cartesian-product-ignore [ignore-sets colls] (cond (some empty? ignore-sets) () ; prune (empty? colls) (list ()) ; base case :else ; recursive case (for [e (first colls) sub-product (cartesian-product-ignore (map (fn [s] (disj se)) ignore-sets) (rest colls))] (cons e sub-product)))) ;; test without any ignore sets (cartesian-product-ignore [] [[:a :b :c] [:d :e] [:f]]) => ((:a :d :f) (:a :e :f) (:b :d :f) (:b :e :f) (:c :d :f) (:c :e :f)) ;; Now the moment of truth (cartesian-product-ignore [(set [:a :e]) (set [:c])] [[:a :b :c] [:d :e] [:f]]) => ((:a :d :f) (:b :d :f) (:b :e :f))
Of course, small changes may be required to meet your specific needs. For example, you might want to accept ignoring sets as a vector or sequence and convert them to internal sets. But this is the essence of the algorithm.
source share