在Clojure中使用递归生成惰性seq?

时间:2016-03-10 12:12:42

标签: loops recursion clojure

我是clojure的新手,目前正在与loop / recur进行斗争。这个问题基本上就是为什么我的'自定义'range func。不返回懒惰序列。我的实现有什么问题,或者你不应该在这种情况下使用递归吗?

(defn my-range
  [nr]
  (loop [l nr acc '()]
    (if (< l 1)
      acc
      (recur (dec l) (conj acc l)))))

当我运行它时:

> (time (take 10 (my-range 100000))) ;; "Elapsed time: 85.443 msecs"
> (time (take 10 (range 100000))) ;; "Elapsed time: 0.095 msecs"

非常大的时间差异使我相信列表首先构建然后10。

1 个答案:

答案 0 :(得分:13)

您不在my-range中使用任何惰性结构。由于您从最后开始组装列表并开始工作,因此访问前十个元素的唯一方法是首先实现所有其他元素。

懒惰的序列从头开始,一直到最后,如下所示:

(defn my-range
  ([end]
   (my-range 1 end))
  ([start end]
   (when (<= start end)
     (lazy-seq (cons start (my-range (inc' start) end))))))

这里的诀窍是你没有返回已实现的序列。而是返回一个存储此函数的对象:

#(cons start (my-range (inc' start) end))

当有人在该对象上调用seq时,它将调用上述函数,缓存其结果并返回该结果。将来调用seq只会返回缓存的结果。但请注意,传递给cons的第二个参数也是一个懒惰序列(因为对my-range的调用会返回lazy-seq),所以它,反过来,只有在必要时才会实现。

为了完整起见,编写此函数的另一种方法如下:

(defn my-range
  [end]
  (take end (iterate inc' 1)))

这可行,因为iteratetake都返回延迟序列。