【问题标题】:What is idiomatic Clojure to "remove" a single instance from many in a list?什么是惯用的 Clojure 从列表中的多个“删除”单个实例?
【发布时间】:2011-10-05 13:57:08
【问题描述】:

我有一个列表,其中可能包含比较相等的元素。我想要一个类似的列表,但删除了一个元素。所以从 (:a :b :c :b :d) 我希望能够“删除”只是 one :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?

【问题讨论】:

  • 作为脚注,我查看了这个问题的 Scala 版本的首选答案的源代码。事实证明,Scala 中的 diff 函数使用可变哈希来计算要从多重集中删除的出现次数。
  • 只是为了澄清:在这里哪个 :b 算作重复有关系吗?从示例输出中,您“删除”了第一个 :b,但它仍然等同于删除了第二个 :b 吗? [abcbd -> acdb,但是 abcd 也可以接受吗?]
  • @monojohnny,abcd 对我的用例同样适用。

标签: list clojure


【解决方案1】:

怎么样:

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

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

【讨论】:

  • 打败我! split-with 在列表中找不到元素的时候做 Right Thing 真的很方便,所以这个解决方案很容易写。
  • 非常优雅。我认为值得注意的是split-with 是建立在take-whiledrop-while 之上的,它们是解决类似问题的更基本的构建块。
  • +1 – 非常优雅!还值得一提的是,这不会删除所有出现的:b,而只会删除第一个(实际上我认为是前者)。文档快速链接:clojure.github.com/clojure/clojure.core-api.html#clojure.core/…
【解决方案2】:

我通常使用像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)

【讨论】:

  • 注意我并不是说这更具可读性,但它更有效,如果你熟悉像 split-with 这样的 HOF 并且需要一些“好的”递归练习,这可能是一个很好的做法代码。
  • 也感谢一个使用惰性序列的好例子! "canonical" explanation of laziness 有点混乱。
【解决方案3】:

令人惊讶的是,没有一个高级 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)

【讨论】:

  • 抱歉,我讨厌这个实现。您正在解决一个非问题 - 我的版本也不会破坏堆栈,而且我不会浪费大量精力调用 reverse - 我从一开始就以正确的方式构建它。
猜你喜欢
  • 2013-12-16
  • 2011-08-03
  • 1970-01-01
  • 2018-05-22
  • 1970-01-01
  • 2011-03-07
  • 1970-01-01
  • 2011-08-13
  • 1970-01-01
相关资源
最近更新 更多