我想实现clojure的范围函数,为什么以下代码不起作用?
(fn [low high]
(loop[low low
ret []]
(if(= low high)
(list ret)
(recur (inc low) (concat ret [low])))))
答案 0 :(得分:4)
我在下面给出了两个正确的实现。你的问题是当你调用(list ret)时,你正在以另一个顺序包装你的累加器(已经是一个列表)。
(defn my-range [low high]
(loop [low low
ret []]
(if (= low high)
(seq ret)
(recur (inc low) (conj ret low)))))
(defn my-range2 [low high]
(take-while
(partial > high)
(iterate inc low)))
修改:range
在其返回的序列中不包含上限参数,partial
中的my-range2
从>=
更改为>
。
答案 1 :(得分:2)
您的代码存在一些问题。
如先前的答案所指出的那样
(list ret)
错了。将其替换为(seq ret)
。注意:要准确返回()
,而不是nil
为空序列,请使用(if (seq ret) (seq ret) ())
。没有惩罚可能使这不必要。 concat
超过了顶部。请改用conj
。另外,
(my-range 10 0)
永远循环。将=
替换为>=
。进行这些更改,
(defn my-range [low high]
(loop [low low, ret []]
(if (>= low high)
(if (seq ret) (seq ret) ())
(recur (inc low) (conj ret low)))))
剩下的问题是这不是懒惰的:它在返回之前开发整个向量。例如,
(take 5 (my-range 42 (java.lang.Integer/MAX_VALUE)))
......似乎需要永远,尽管你只需要五个元素。
是一个简单的懒惰版本(defn my-range [low high]
(lazy-seq
(if (>= low high) () (cons low (my-range (inc low) high)))))
然后
(take 5 (my-range 42 (java.lang.Integer/MAX_VALUE)))
;(42 43 44 45 46)
......立竿见影。
RedDeckWins's my-range2
同样适用,而且更整洁。
编辑:range
在(if (seq ret) (seq ret) ())
的第一个实现中返回一个列表,而不是向量,将return语句更改为my-range
。
答案 2 :(得分:1)
您的代码没有问题,(list ret)
部分除外。
list
function使用给定元素创建新列表(在您的示例中,使用单个ret
元素)。如果您想转换顺序,则应使用seq
function代替。
我还建议你不要在这里使用concat
function,因为它很懒,可能导致产生StackOwerflowError
长序列。
其余代码没问题。
有关工作示例,请参阅RedDeckWins's answer。