我想创建一个函数,返回一个延迟无限的Fibonacci数无限序列。
现在,我可以在顶级命名空间中使我的序列可用,如下所示:
(def fibonacci-numbers
(lazy-cat [0 1] (map + fibonacci-numbers (rest fibonacci-numbers))))
然而,这意味着如果我开始消耗它们,我将无法控制垃圾收集。
我希望做类似的事情:
(defn fibonacci-numbers-fn []
(lazy-cat [0 1] (map + (fibonacci-numbers-fn) (rest (fibonacci-numbers-fn)))))
这显然不起作用,因为我最终会创建O(2 ^ n)序列。我想我问的是如何在函数本地命名空间中创建一个自引用的延迟序列。我该怎么办?
编辑:虽然我喜欢amalloy发布的流行解决方案并且在互联网上发现defn fibs [] (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
,但我对类似于规范的Haskell方式的版本感兴趣:
fibonaccis = 0 : 1 : zipWith (+) fibonaccis (tail fibonaccis)
这是我用原始功能试图完成的。对我来说,map-iterate解决方案读起来像“添加前两个元素以创建一个新元素”,而懒惰猫解决方案读起来就像“加入一个具有第一个滞后的流”。如何在没有顶级命名空间中的序列的情况下“加入具有第一个延迟的流”?
答案 0 :(得分:4)
(take 10 (map first (iterate (fn [[a b]]
[b (+ a b)])
[0 1])))
;; (0 1 1 2 3 5 8 13 21 34)
或者如果您已经开始手动使用lazy-seq:
(letfn [(fibs
([]
(fibs 0 1))
([a b]
(lazy-seq
(cons a (fibs b (+ a b))))))]
(take 10 (fibs)))
;; (0 1 1 2 3 5 8 13 21 34)
答案 1 :(得分:3)
如果在[]之前放置一个可选名称,则fn
形式定义的函数可以是递归的。 (在此示例中,使用的名称为this
)
user> (defn fibonacci-numbers []
((fn this [a b] (lazy-seq (cons a (this b (+ a b))))) 0 1))
user> (take 10 (fibonacci-numbers))
(0 1 1 2 3 5 8 13 21 34)
产生序列的实际函数是匿名函数,每次调用它时只生成下一个元素。没有堆栈或堆溢出的可能性(除非你在一个var中保存封闭函数的返回值)
答案 2 :(得分:1)
我有一个非常相似的问题,并最终选择了以下宏(这基本上是一些糖,而不是合金和承诺的答案):
(defmacro rec-seq [name expr]
`(let [p# (promise)
s# (lazy-seq (let [~name @p#] ~expr))]
(deliver p# s#)
s#))
然后让你写:
(defn fibonacci-numbers-fn []
(rec-seq fibs (lazy-cat [0 1] (map +' fibs (rest fibs)))))
这几乎就是你想写的。
PS:rec-seq是recursive-seq的缩写。
答案 3 :(得分:0)
您可以使用promise
打结,手动执行haskell自动执行的操作:
(defn fibs []
(let [fibs (promise)]
@(doto fibs
(deliver (list* 0 1 (lazy-seq (map +' @fibs (rest @fibs))))))))
答案 4 :(得分:-1)
也许letfn
正是您要找的?
(def fibo-nums
(letfn [(fibo-num-fn []
(lazy-cat [0 1]
(map + (fibo-num-fn) (rest (fibo-num-fn)))))]
fibo-num-fn))