另一个答案为您提供了 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)))))
作为最后一个建议,这是一个解决方案,它通过在更高级别上解决问题,与另一个答案的一半接近,但它仅使用核心函数 reverse 和 map,将函数应用于每个序列元素,但自身不深度递归:
(defn deep-reverse [coll]
(reverse (map #(if (coll? %) (deep-reverse %) %) coll)))