【问题标题】:Tail recursive Clojure function for computing combinations用于计算组合的尾递归 Clojure 函数
【发布时间】:2023-03-10 02:02:01
【问题描述】:

我有一个函数,给定一个向量,返回所有无序组合:

(defn combination [ps]
  (loop [acc []
         ps ps]
    (if (= 2 (count ps))
      (conj acc (apply vector ps))
      (recur (apply conj acc (map #(vector (first ps) %) (rest ps)))
             (rest ps)))))

这很好用,但对我来说似乎有点令人费解。

在 Clojure 中是否有更直接、更惯用的方式来完成此任务?我很高兴使用 Clojure 核心或库函数,因为这肯定是我对“惯用”定义的一部分。 :)

【问题讨论】:

  • 您可以将(apply vector ps)替换为(vec ps)
  • ...(count ps) 小于 2 时崩溃。(case (count ps), (0 1) (), 2 (conj ... ), (recur ... )) 可能。
  • 好点。我只是在查看代码并认为(= 2 ...) 有点代码味道。你会很高兴知道我在程序中用你的函数替换了我的函数,这导致我提出这个问题。 :)

标签: recursion clojure functional-programming tail-recursion


【解决方案1】:

您的代码会按顺序返回对两个元素的所有选择。另一种方法是......

(defn combination [s]
  (let [tails (take-while next (iterate rest s))]
    (mapcat (fn [[f & rs]] (map #(vector f %) rs)) tails)))

这比你的短,也很懒惰。但它可能会更慢。

【讨论】:

  • 速度对我来说不是问题,所以效果很好。
  • 当我再看一遍时,算法几乎和你的一样。为了简洁起见,我在内部循环中使用了解构。 tails 的表达式替换了您的 loop/recur 和您的终止测试。而且,由于我自始至终都在处理惰性序列,因此我不需要在向量或其他任何东西中累积解决方案。
  • 是的,您的代码非常简洁。 Clojure 的解构是很棒的东西!
【解决方案2】:

有点滑稽……

(defn combination [ps]
  (clojure.math.combinatorics/combinations ps 2))

...这是懒惰的,但the source code 是你的两到三倍。

【讨论】:

  • 缩略图,你确定这不是我的代码所做的吗?它肯定返回与clojure.math.combinatorics/combinations 相同的值。
  • 没有。我将删除我的误导性 - 好的,完全错误的 - 评论。我在没有尝试或阅读给定示例的情况下误解了库函数。啧啧啧!
【解决方案3】:

Clojure 有 clojure.math.combinatorics,其中包含许多方便的功能。因此,可以说,在 Clojure 中执行操作的“惯用”方式是导入/要求 clojure.math.combinatorics,然后简单地使用 n = 2 调用组合。

...>  (comb/combinations [1 2 3 4] 2)
((1 2) (1 3) (1 4) (2 3) (2 4) (3 4))

为此,您需要先添加正确的依赖项。

在我写这篇文章时,最新版本是:[org.clojure/math.combinatorics "0.0.7"]

然后我确实需要它“:as comb”:

(:require [clojure.math.combinatorics :as comb]

如果您不想使用 math.combinatorics,您可以编辑您的问题以使其准确,我将删除我的答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-29
    • 2011-08-03
    • 1970-01-01
    • 2013-05-15
    • 1970-01-01
    • 1970-01-01
    • 2011-02-28
    • 1970-01-01
    相关资源
    最近更新 更多