【问题标题】:Function-local, self-referential, lazy fibonacci sequence函数局部、自引用、惰性斐波那契数列
【发布时间】:2012-09-25 19:32:26
【问题描述】:

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

现在,我可以像这样在顶级命名空间中提供我的序列:

(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 解决方案读作“加入具有第一个延迟的流”。如何在没有顶级命名空间中的序列的情况下“加入具有第一个滞后的流”?

【问题讨论】:

  • 您的 defn 需要 [] 但所以不允许我进行 2 个字符的编辑。 (╯°□°)╯︵ ┻━┻

标签: clojure fibonacci local-variables


【解决方案1】:
(take 10 (map first (iterate (fn [[a b]]
                               [b (+ a b)])
                             [0 1])))

;; (0 1 1 2 3 5 8 13 21 34)

或者,如果您打算手动使用惰性序列:

(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)

【讨论】:

    【解决方案2】:

    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 中)

    【讨论】:

    • 这是对最初提出的问题的一个很好的解决方案。你能想出一种方法让代码按照我的编辑来阅读吗?
    【解决方案3】:

    我遇到了一个非常相似的问题,最终选择了以下宏(这基本上是 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 的缩写。

    【讨论】:

      【解决方案4】:

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

      (defn fibs []
        (let [fibs (promise)]
          @(doto fibs
             (deliver (list* 0 1 (lazy-seq (map +' @fibs (rest @fibs))))))))
      

      【讨论】:

        【解决方案5】:

        也许letfn 就是您要找的东西?

        (def fibo-nums
          (letfn [(fibo-num-fn []
                    (lazy-cat [0 1]
                      (map + (fibo-num-fn) (rest (fibo-num-fn)))))]
            fibo-num-fn))
        

        【讨论】:

        • 如果你走这条路,我想你最终会遇到我上面提出的同样问题;您不断调用该函数并创建新序列。有人可以确认吗?我认为您也缺少 [] 并且需要在最后调用 fibo-num-fn 而不是返回函数本身:)。
        • 好吧,我想我没有完全理解你的问题。
        猜你喜欢
        • 2018-10-08
        • 1970-01-01
        • 2018-07-07
        • 1970-01-01
        • 2017-07-08
        • 2014-05-03
        • 2015-06-05
        相关资源
        最近更新 更多