深反向Clojure

时间:2013-10-03 03:19:51

标签: clojure reverse

我正在尝试在clojure中实现深度逆转。如果lst是(1(2(3 4 5))(2 3)),它应该返回((3 2)((5 4 3)2)1)。
这是我到目前为止:

defn dRev [lst]
    ( if (= lst ())
        nil
        ( if (list? (first lst))
            ( dRev (first lst) )
            ( concat
                ( dRev (rest lst)) (list (first lst))
            )
        )
   )
)

但是,我的实现仅在嵌套列表是最后一个元素时才有效,但结果列表也是平展的。

例如:(dRev'(1 2(3 4))将返回(4 3 2 1) 否则,例如:(dRev'(1(2 3)4))将仅返回(3 2 1)。

我现在打了这个砖墙一段时间了,我无法找到我的代码问题。
有人可以帮帮我吗?

3 个答案:

答案 0 :(得分:7)

另一个答案为您提供了Clojure中深度反转的最佳实现,因为它使用了clojure.walk/postwalk函数,该函数概括了将函数深度应用于集合的每个元素的问题。在这里,我将向您介绍您发布的实施问题。

首先,不寻常的格式化使得很难发现正在发生的事情。以下是固定格式的相同内容:

(defn dRev [lst]
  (if (= lst ())
    nil
    (if (list? (first lst))
        (dRev (first lst))
        (concat (dRev (rest lst))
                (list (first lst))))))

接下来,还有其他一些尚未解决此问题的小修补程序:

  • 更改函数名称以符合Clojure约定(连字符而非camel-case),
  • 使用通常的Clojure默认名称来收集参数coll而不是lst
  • 使用empty?检查空集合,
  • 在默认情况下返回()因为我们知道要返回列表而不是其他类型的seq,
  • 并使用coll?代替list?因为我们也可以反转任何集合而不仅仅是列表:

(如果你真的想要只反转列表并保留所有其他集合,请反转最后一次更改。)

(defn d-rev [coll]
  (if (empty? coll)
    ()
    (if (coll? (first coll))
        (d-rev (first coll))
        (concat (d-rev (rest coll))
                (list (first coll))))))

现在,格式化修复显示了实现的主要问题:在递归调用((d-rev (first coll)) resp。(dRev (first lst)))中,只返回该递归的结果,但是你忘记了处理列表的其余部分。基本上,您需要做的是处理集合的其余部分始终相同,并且仅根据第一个元素是否为list resp来更改处理第一个元素的方式。是否收集:

(defn d-rev [coll]
  (if (empty? coll)
    ()
    (concat (d-rev (rest coll))
            (list (if (coll? (first coll))
                    (d-rev (first coll))
                    (first coll))))))

这是一个有效的解决方案。

尽管效率非常低,因为concat完全重建了每个元素的列表。通过使用尾递归算法可以获得更好的结果,这非常简单(因为序列上的尾递归反转元素的顺序很自然):

(defn d-rev [coll]
  (loop [coll coll, acc ()]
    (if (empty? coll)
      acc
      (recur (rest coll)
             (cons (if (coll? (first coll))
                     (d-rev (first coll))
                     (first coll))
                   acc)))))

作为最后一个建议,这里有一个解决方案,通过在更高层次上解决问题来解决另一个问题的解决方案,但它仅使用适用的核心函数reversemap对序列的每个元素都有一个函数,但它本身不会深度递归:

(defn deep-reverse [coll]
  (reverse (map #(if (coll? %) (deep-reverse %) %) coll)))

答案 1 :(得分:5)

您可以使用clojure.walk/postwalkclojure.core/reverse构建您正在撰写的内容。这会对树输入执行深度优先遍历,并反转它找到的任何序列。

(defn dRev [lst]
  (clojure.walk/postwalk #(if (seq? %) (reverse %) %) lst))

答案 2 :(得分:0)

如果输入以下内容,这是我的问题版本:

(deep-reverse '(a (b c d) 3))

返回

=> '(3 (d c b) a)

问题来自Ninety-Nine Lisp Problems

我的代码最终是这样的,但是,它们可能是更好的实现,这个工作正常。

(defn deep-reverse
  "Returns the given list in reverse order. Works with nested lists."
  [lst]
  (cond
    (empty? (rest lst))   lst
    (list? (first lst))   (deep-reverse(cons (deep-reverse (first lst)) (deep-reverse (rest lst))))

    :else
    (concat (deep-reverse (rest lst)) (list (first lst)))))

希望这就是你要找的东西!