什么是惯用的Clojure从列表中的许多人“删除”单个实例?

时间:2011-10-05 13:57:09

标签: list clojure

我有一个列表,其中可能包含比较相等的元素。我想要一个类似的列表,但删除了一个元素。所以从(:a:b:c:b:d)我希望能够“删除”一个:b来获取(:a:c:b:d)。

上下文是纸牌游戏中的一手牌,其中有两副标准牌正在播放,因此可能存在重复的牌但仍然一次只能玩一张。

我有工作代码,见下文。在Clojure中有更多惯用的方法吗?

(defn remove-one [c left right]
  (if (= right ())
    left
    (if (= c (first right))
      (concat (reverse left) (rest right))
      (remove-one c (cons (first right) left) (rest right)))))

(defn remove-card [c cards]
  (remove-one c () cards))

以下是我之前收到的Scala答案:What is an idiomatic Scala way to "remove" one element from an immutable List?

3 个答案:

答案 0 :(得分:24)

怎么样:

(let [[n m] (split-with (partial not= :b) [:a :b :c :b :d])] (concat n (rest m)))

将列表拆分为:b,然后删除:b并连接两个列表。

答案 1 :(得分:9)

我通常使用像split-with这样的高阶函数解决这些问题,但有人已经这样做了。有时它在更原始的层次上工作更具可读性或更高效,所以这里是原始循环代码的更好版本,使用延迟序列并进行推广以获取删除谓词而不是限制为相等检查:

(defn remove-once [pred coll]
  ((fn inner [coll]
     (lazy-seq
      (when-let [[x & xs] (seq coll)]
        (if (pred x)
          xs
          (cons x (inner xs))))))
   coll))


user> (remove-once #{:b} [:a :b :c :b :d])
(:a :c :b :d)

答案 2 :(得分:1)

令人惊讶的是,没有一个高级API可以做这样的事情。这是另一个类似于@amalloy和@James的版本,它使用recur以便不堆叠溢出。

(defn remove-once [x c]                                                                                                                                                                                                                     
  (letfn [(rmv [x c1 c2 b]                                                                                                                                                                                                                  
            (if-let [[v & c] (seq c1)]                                                                                                                                                                                                      
              (if  (and (= x v) b)                                                                                                                                                                                                          
                (recur x c c2 false)                                                                                                                                                                                                        
                (recur x c (cons v c2) b))                                                                                                                                                                                                  
              c2))]                                                                                                                                                                                                                         
    (lazy-seq (reverse (rmv x c '() true)))))                                                                                                                                                                                               

(remove-once :b [:a :b :c :b :d])
;; (:a :c :b :d)