4

我想创建一个返回延迟扩展的无限斐波那契数序列的函数。

现在,我可以让我的序列在顶级命名空间中可用,如下所示:

(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 解决方案读起来像“添加前两个元素以创建一个新元素”,而lazy-cat 解决方案读起来像“加入具有第一个滞后的流”。如何在没有顶级命名空间中的序列的情况下“加入具有第一个滞后的流”?

4

5 回答 5

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)
于 2012-09-25T21:39:03.187 回答
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 中)

于 2012-09-25T21:51:52.193 回答
1

我有一个非常相似的问题,最终选择了以下宏(这基本上是 amalloy 涉及承诺的答案的一些糖):

(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 的缩写。

于 2015-08-30T22:04:27.707 回答
0

您可以使用 apromise来打结,手动执行 haskell 自动执行的操作:

(defn fibs []
  (let [fibs (promise)]
    @(doto fibs
       (deliver (list* 0 1 (lazy-seq (map +' @fibs (rest @fibs))))))))
于 2015-08-29T21:49:29.257 回答
-1

也许letfn是您正在寻找的东西?

(def fibo-nums
  (letfn [(fibo-num-fn []
            (lazy-cat [0 1]
              (map + (fibo-num-fn) (rest (fibo-num-fn)))))]
    fibo-num-fn))
于 2012-09-25T20:49:50.153 回答