另一个答案为您提供了 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)))