【问题标题】:What is wrong with my Clojure implementation of permutations我的 Clojure 实现排列有什么问题
【发布时间】:2014-10-20 22:41:59
【问题描述】:

我知道有多种方法可以使用 Clojure 解决排列问题。 我尝试使用 Core.Logic 创建 DCG(定句语法),但是 库的 DCG 部分太实验性了,没有用。

在下面的代码中,我尝试了两种不同的方法。一个是列表推导(注释掉),类似于我在 Haskell 中解决这个问题的方式。

第二种方法使用 MapCat 将 cons/first 应用于每个返回值 递归调用排列。删除项目确保我不会在每个位置多次使用相同的字母。

谁能解释一下列表理解方法有什么问题以及 MapCat 方法有什么问题。在 Haskell 中推理这类问题要容易得多 - 我是否缺少关于 Clojure 的一些观点?

(defn remove-item [xs]
   (remove #{(first xs)} xs )
)

(defn permutation [xs]

  (if (= (count xs) 1)
      xs

     ;(for [x xs y (permutation (remove-item xs))
     ;          :let [z (map concat y)]]
     ;          z)                    

     (mapcat #(map cons first (permutation (remove-item %)) ) xs)

  )
)

编辑:@thumbnail 已经解决了 cmets 中的 MapCat 子问题

【问题讨论】:

  • DCG 是 TLA 对吧? recPermute 是做什么的?
  • @amalloy - 我的问题不是关于如何解决 Clojure 中的排列。除了 stackoverflow 响应之外,网上还有大量的示例排列代码。我的问题是为什么我尝试的实现不起作用 - 我缺少什么更深层次的概念。很多人在这里提出半生不熟的问题 - 这是了解 clojure 工作原理的真正尝试和真正的问题。
  • @stevemacn 您可以随时使用更具体的标题再次提问,例如“我的 Clojure 排列实现有什么问题?”
  • @stevemacn (partial cons x) 等价于#(cons x %)(fn [coll] (cons x coll))。它将conses x 放到它的顺序参数上,map 贯穿perms 中的所有其他s。封闭的mapcat 连接将此过程应用于s 的每个元素的结果,被视为一个序列。因此,该函数接受一个 set 参数,但返回一个 sequences 序列。你应该知道 - 基本上是你写的 :)。
  • @stevemacn 所以你对(partial cons x) 所做的事情是正确的;和(disj s x) 返回 设置s 没有元素x。顺便说一句,我从您的代码中得到的印象是 Haskell 可以将非空集合解构为任意成员 x 和剩余的 set xs。在 Clojure 中试试这个,xs 将是一个序列,而不是一个集合。您可以在ClojureDocsGrimoire 查找标准函数,例如partialconj。最后,如果您想继续这样做,请遵循 @amalloy 的隐含建议并在其他地方重新打开修改后的问题。

标签: clojure functional-programming clojure-core.logic


【解决方案1】:

我们可以将permutation函数简化为

(defn permutation [xs]
  (if (= (count xs) 1)
    xs
    (for [x xs
          y (permutation (remove-item xs))]
      (map concat y))))

尝试在任何复数形式上使用它都会产生java.lang.IllegalArgumentException: Don't know how to create ISeq from: ... 任何你想要置换的东西。

有两个错误:

  • permutation 应该返回 一个序列 的序列,即使有 只有其中之一;所以xs 应该是(list xs)。这就是导致异常的原因。
  • 给定xxs 的排列,鉴于此,没有xxs 的排列y 就是(cons x y)

修正了这些,我们有

(defn permutation [xs]
  (if (= (count xs) 1)
    (list xs)
    (for [x xs
          y (permutation (remove-item x xs))]
     (cons x y))))

例如,

(permutation (range 3))
;((0 1 2) (0 2 1) (1 0 2) (1 2 0) (2 0 1) (2 1 0))

只有当所有排列的东西都不同时,上述方法才有效。在另一个极端......

(permutation [1 1 1])
;()

还有,

  • count 扫描整个序列。找出是否只有 一个元素,(seq (rest xs))(= (count xs) 1) 快。
  • remove 中的remove-item 扫描整个序列。有 我们无能为力。

如果我们知道我们正在处理不同的事物,那么将它们作为一个集合来处理会更简单快捷:

(defn perm-set [xs]
  (case (count xs)
    0 '()
    1 (list (seq xs))
    (for [x xs, y (perm-set (disj xs x))]
      (cons x y)))
  • 它也适用于空集。
  • count 是即时的,disj 几乎是恒定的时间,所以这是 更快。

因此:

(perm-set (set '()))
;()

(perm-set (set (range 3)))
;((0 1 2) (0 2 1) (1 0 2) (1 2 0) (2 0 1) (2 1 0))

【讨论】:

    【解决方案2】:

    我们可以通过使用原始序列中项目的索引来添加对重复项的支持。函数 append-index 返回一个新序列,其中索引和值现在位于向量中。例如'(\a \b \c) -> '([0 \a] [1 \b] [2 \c] [3 \a])。

    然后,您在 for 循环中使用此序列,当我们想要将其从原始序列中删除时获取项目的索引,并在我们将其转换为尾部序列时获取值。

    (defn remove-nth [coll n]
      (into (drop (inc n) coll) (reverse (take n coll))))
    
    (defn append-index [coll]
      (map-indexed #(conj [%1] %2) coll))
    
    (defn permutation [xs]
      (let [i-xs (append-index xs)]
      (if (= (count xs) 1)
        (list xs)
        (for [x i-xs
              y (permutation (remove-nth xs (first x)))]
          (cons (last x) y)))))
    

    感谢上一篇文章,我自己也在为排列问题苦苦挣扎,没有考虑过使用 for 理解。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-07
      • 2021-07-25
      • 2011-09-08
      • 2016-05-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多