在clojure中的两个集合之间实现XOR

时间:2015-02-13 04:45:17

标签: recursion clojure functional-programming

我正在努力学习Clojure - 而且我非常环保。我试图在两个集合之间实现集合差异的递归版本。

我想要做的是先将两个列表组合在一起,将组合转换为一个集合以删除重复项,然后将混乱程序传回其自身并检查组合集合的第一个元素是否同时输入名单。如果它将组合集合的其余部分传回原始集合并重复该过程......但它总是传回一个空列表。我没有假设代码是那么好。我是函数式编程的新手。

也许我没有正确使用逻辑?我做了一个测试,普通榆树并没有开始空,但也许它早早就有一个空列表?任何帮助将非常感激。感谢。

(defn alone 
([l1 l2]
    (cond (empty? l1) l2
      (empty? l2) l1
      :else (alone (vec (set (into l1 l2))) '() l1 l2)))

([common-elms return-list l1 l2]
    "common-elms = set of common elements
     return-list = list of XOR items 
     l1 = list 1
     l2 = list 2 "
    (cond (empty? common-elms) return-list
          (and (contains? (first common-elms) l1) (contains? (first common-elms) l2))
            (alone (rest common-elms) return-list l1 l2)
          :else (alone (rest common-elms) return-list l1 l2))))

3 个答案:

答案 0 :(得分:3)

甚至更简单,使用clojure.set命名空间:

(defn xor-list [l1 l2]
  (let [s1 (set l1)
        s2 (set l2)]
    (seq (clojure.set/difference 
           (clojure.set/union s1 s2) 
           (clojure.set/intersection s1 s2)))))

但它只是返回列表的两个参数的函数。对于返回集合的任意数量的集合,clojure.set/xor的一般情况{@ 1}}应该增强。

答案 1 :(得分:3)

首先,@ivanpierre解决方案肯定是最好的,因为它实际上使用symmetric difference的第一个属性并利用clojure.set。但是你自己的基于递归的解决方案是有效的,尽管代码中存在一些缺陷。修改后的版本及解释:

(defn alone
  ([l1 l2]
   (cond (empty? l1) l2
         (empty? l2) l1
         :else (alone (vec (set (into l1 l2))) '() l1 l2)))

  ([common-elms return-list l1 l2]
   "common-elms = set of common elements
     return-list = list of XOR items
     l1 = list 1
     l2 = list 2 "
   (cond (empty? common-elms) return-list
         (let [ce (first common-elms)]   ;; see [1]
           (and (some #{ce} l1)
                (some #{ce} l2)))
         (alone (rest common-elms) 
                return-list 
                l1 l2)
         :else (alone (rest common-elms) 
                      (conj return-list (first common-elms)) ;; see [2] 
                      l1 l2))))

user> (alone '(1 2 3 4 5) '(3 4 5 6 7))
(2 6 1 7)
user> 
  1. 你必须要知道contains?仅适用于关联集合,如地图或向量(向量是一个关联集合,其键是索引)。请参阅this问题。 如果some包含给定元素,则:elseconj提供了在任何类型的集合中进行测试的惯用方法。

  2. 如果找不到公共元素(return-list),您忘记loop/recur元素到(defn xor ([l1 l2] (loop [l1 (set l1) l2 (set l2) xor '()] (if (seq l1) (let [e (first l1)] (if (l2 e) ;; l2 is a set, test if e is in l2 (recur (rest l1) (disj l2 e) xor) (recur (rest l1) l2 (conj xor e)))) (reduce conj xor l2)))) ;; when l1 is empty, add all element left in l2 ([l1 l2 & more] (reduce xor (xor l1 l2) more))) user> (let [l1 '(1 1 2 3 4 5) l2 '(3 4 5 5 6 7) l3 '(1 10 11 13) l4 '(1 2 11 6 6)] (println (xor l1 l2 l3 l4))) (1 10 13 7) nil

  3. 注意:风格比Clojurian更柔和;要保持基于递归的解决方案,您可以使用{{1}}。以下是N列表的一般解决方案:

    {{1}}

答案 2 :(得分:1)

您是否有理由以递归方式执行此操作?如果这不是必需的,请使用clojure.set命名空间:

(defn xor-list [l1 l2]
  (let [s1 (set l1)
        s2 (set l2)
        i (clojure.set/intersection s1 s2)]
    (seq (clojure.set/union
          (clojure.set/difference s1 i)
          (clojure.set/difference s2 i)))))