【问题标题】:How to define a general recurrence function in Clojure如何在 Clojure 中定义一个通用的递归函数
【发布时间】:2014-12-14 00:43:42
【问题描述】:

我对 Clojure 中递归关系的通用函数有了一个想法:

(defn recurrence [f inits]
  (let [answer (lazy-seq (recurrence f inits))
        windows (partition (count inits) 1 answer)]
    (concat inits (lazy-seq (map f windows)))))

然后,例如,我们可以将斐波那契数列定义为

(def fibs (recurrence (partial apply +) [0 1N]))

这对小数字很有效:

(take 10 fibs)
;(0 1N 1N 2N 3N 5N 8N 13N 21N 34N)

但如果要求实现长序列,它会炸毁堆栈:

(first (drop 10000 fibs))
;StackOverflowError ...

有什么办法可以克服吗?

【问题讨论】:

    标签: clojure recurrence


    【解决方案1】:

    这里的问题是,您在每次迭代时都在建立对 concat 的调用,而 concat 调用会建立一大堆未评估的 thunk,当您最终要求一个值时它们会爆炸。通过使用 cons 并且只传递所需的值计数(和concat,但不是递归堆栈吹concat),我们得到了一个表现更好的惰性序列:

    user> 
    (defn recurrence
      [f seed]
      (let [step (apply f seed)
            new-state (concat (rest seed) (list step))]
        (lazy-seq (cons step (recurrence f new-state)))))
    #'user/recurrence
    user> (def fibs (recurrence +' [0 1]))
    #'user/fibs
    user> (take 10 fibs)
    (1 2 3 5 8 13 21 34 55 89)
    user> (first (drop 1000 fibs))
    113796925398360272257523782552224175572745930353730513145086634176691092536145985470146129334641866902783673042322088625863396052888690096969577173696370562180400527049497109023054114771394568040040412172632376N
    

    【讨论】:

    • 一个想法:如果我们对状态使用双端队列,我们​​可以删除 concat 的使用,并使函数更加高效(从而允许有效地删除状态中最旧的元素,并有效地添加最新的)
    • 感谢您也解释了失败的原因。
    • 我已经尝试过您使用队列的建议 - 我认为不需要双端队列。见here
    【解决方案2】:

    the accepted answer开始。

    • 我们希望以seed 开始序列。
    • 正如作者所建议的,我们使用队列来提高效率。不需要双端队列:我们只需要 clojure 的 PersistentQueue

    改编后的recurrence 可能如下所示:

    (defn recurrence
      [f seed]
      (let [init-window (into (clojure.lang.PersistentQueue/EMPTY) seed)
            unroll (fn unroll [w] (lazy-seq (cons
                                              (peek w)
                                              (unroll (-> w
                                                          pop
                                                          (conj (apply f w)))))))]
        (unroll init-window)))
    

    ...和以前一样...

    (def fibs (recurrence +' [0 1]))
    

    然后

    (take 12 fibs)
    ;(0 1 1 2 3 5 8 13 21 34 55 89)
    

    (first (drop 10002 fibs))
    ;88083137989997064605355872998857923445691333015376030932812485815888664307789011385238647061572694566755888008658862476758094375234981509702215595106015601812940878487465890539696395631360292400123725490667987980947195761919733084221263262792135552511961663188744083262743015393903228035182529922900769207624088879893951554938584166812233127685528968882435827903110743620870056104022290494963321073406865860606579792362403866826411642270661211435590340090149458419810817251120025713501918959350654895682804718752319215892119222907223279849851227166387954139546662644064653804466345416102543306712688251378793506564112970620367672131344559199027717813404940431009754143637417645359401155245658646088296578097547699141284451819782703782878668237441026255023475279003880007450550868002409533068098127495095667313120369142331519140185017719214501847645741030739351025342932514280625453085775191996236343792432215700850773568257988920265539647922172315902209901079830195949058505943508013044450503826167880993094540503572266189964694973263576375908606977788395730196227274629745722872833622300472769312273603346624292690875697438264265712313123637644491367875538847442013130532147345613099333195400845560466085176375175045485046787815133225349388996334014329318304865656815129208586686515835880811316065788759195646547703631454040090435955879604123186007481842117640574158367996845627012099571008761776991075470991386301988104753915798231741447012236434261594666985397841758348337030914623617101746431922708522824868155612811426016775968762121429282582582088871795463467796927317452368633552346819405423359738696980252707545944266042764236577381721803749442538053900196250284054406347238606575093877669323501452512412179883698552204038865069179867773579705703841178650618818357366165649529547898801198617541432893443650952033983923542592952070864044249738338089778163986683069566736505126466886304227253105034231716761535350441178724210841830855527586882822093246545813120624113290391593897765219320931179697869997243770533719319530526369830529543842405655495229382251039116426750156771132964376N
    

    另一种方式,基于从 Clojure 的喜悦 偷来的一个想法 - 我认为是......

    (defn recurrence
      [f seed]
      (let [init-window (into (clojure.lang.PersistentQueue/EMPTY) seed)
            windows (iterate
                      (fn [w] (-> w, pop, (conj (apply f w))))
                      init-window)]
        (map peek windows)))
    

    【讨论】:

      猜你喜欢
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-09
      • 2015-11-20
      相关资源
      最近更新 更多