【问题标题】:Deep-Reverse Clojure深度反向 Clojure
【发布时间】:2013-10-03 03:19:51
【问题描述】:

我正在尝试在 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)。

我现在碰到了这堵砖墙一段时间,但我无法找出我的代码的问题。
谁能帮帮我?

【问题讨论】:

  • 请看一个合适的 lisp 编辑器。你不应该依赖括号,而应该依赖适当的缩进,就像 Vim 或 Emacs lisp 插件提供的那样。我可能与这个问题无关,但它会在处理更复杂的 lisp 补丁时为您提供帮助。

标签: clojure reverse


【解决方案1】:

另一个答案为您提供了 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 约定(连字符而不是驼峰式),
  • 对集合参数使用常用的 Clojure 默认名称 coll 而不是 lst
  • 使用empty? 检查空集合,
  • 在默认情况下返回(),因为我们知道我们想要返回一个列表而不是其他类型的序列,
  • 并使用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))),您只返回该递归的结果,但您忘记处理其余部分的名单。基本上,您需要做的是始终以相同的方式处理集合的其余部分,并且仅根据第一个元素是否为列表来更改处理第一个元素的方式。收藏与否:

(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)))

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 嗨,Jared,我非常感谢您的帮助,但我对 clojure 很陌生。有什么方法可以让我坚持我目前的理解来解决问题?
    【解决方案3】:

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

    (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)))))
    

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

    【讨论】:

      猜你喜欢
      • 2023-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多