我刚开始在我的clojure旅程中,我想知道是否有人可以指出我的初学者错误在我的功能下面只是反转列表。我知道已经存在反向功能,所以这纯粹是为了学习。
(defn rev
([l]
(if (= (count l) 1) l (rev '() l)))
([l orig]
(if (= (count orig) 0)
l
(rev (conj l (first orig)) (rest orig)))))
在我的辩护中,它确实有效,但是我发现自己在clojure中做了很多工作,当我需要一个工作列表(如本例中conj
新项目时
答案 0 :(得分:6)
首先,首先查看reverse函数的现有实现是一个非常好的主意:
(defn reverse [coll]
(reduce conj () coll))
与您的代码的主要区别在于reverse
的现有实现使用了高阶函数reduce。尽可能使用高阶函数而不是递归是一种很好的做法。
-
但我们假设您的目标是学习递归。我就是这样写的:
(defn rev
([coll]
(rev coll ()))
([coll acc]
(if-let [[h & ts] (seq coll)]
(recur ts (conj acc h))
acc)))
让我们仔细看看我的代码。
首先,我使用if-let和seq来检查coll
是否为非空集合。
然后我使用destructuring来获取给定集合的第一个元素以及其余部分。
换句话说,我的构造
(if-let [[h & ts] (seq coll)]
(recur ts (conj acc h))
acc)
可以使用if
,let
,first
和rest
进行重写:
(if-not (empty? coll)
(let [h (first coll)
ts (rest coll)]
(recur ts (conj acc h)))
acc)
这几乎就是你自己写的。
最后一件重要的事情是我正在使用recur而不是直接调用rev
。
它允许clojure编译器执行尾递归优化。
您还应该考虑使用loop而不是创建重载函数,除非您想要将两个参数形成为public:
(defn rev [coll]
(loop [coll coll
acc ()]
(if-let [[h & ts] (seq coll)]
(recur ts (conj acc h))
acc)))
-
因此,您的代码中有很多需要改进的地方。但我只能看到三个真正的错误:
recur
; (= (count l) 1)
; (= (count orig) 0)
。我在你的代码中解决了这两个错误:
(defn rev
([l]
(rev '() l))
([l orig]
(if (empty? orig)
l
(recur (conj l (first orig)) (rest orig)))))
答案 1 :(得分:2)
如果您不想将该功能公开给其他arities,请在本地定义和使用它们:
(defn rev [l]
((fn rev2 [l orig]
(if (empty? orig)
l
(rev2 (conj (first orig) l) (rest orig))))
() l))
您可能会发现使用let
或letfn
(defn rev [l]
(letfn [(rev2 [l orig]
(if (empty? orig)
l
(rev2 (conj l (first orig)) (rest orig))))]
(rev2 () l)))
顺便说一下,......
seq
或empty?
。()
不需要引用。当然,我们可以而且应该使用recur
而不是对rev2
的递归调用,以避免在长序列上吹掉堆栈:
(defn rev [l]
(letfn [(rev2 [l orig]
(if (empty? orig)
l
(recur (conj l (first orig)) (rest orig))))]
(rev2 () l)))