Recursion 继续传球的方式似乎对Clojure没有什么影响

Recursion 继续传球的方式似乎对Clojure没有什么影响,recursion,clojure,continuations,continuation-passing,Recursion,Clojure,Continuations,Continuation Passing,我一直在试验延续传递样式,因为我可能很快需要处理一些非尾部递归函数。无论如何,要知道这是一个很好的技巧!我用Lua和Clojure编写了一个测试函数;在我的小Android掌上电脑上运行Lua one Lua版本似乎工作得很好,Lua的堆栈已经有了大约300000的深度,但是有了CPS,我很容易在系统崩溃之前完成了7000000次迭代,可能是因为内存不足,而不是CPS/Lua组合的任何限制 Clojure的尝试没有那么成功。在1000次迭代中,它抱怨堆栈被破坏了,它可以用普通迭代做得更好,普通迭

我一直在试验延续传递样式,因为我可能很快需要处理一些非尾部递归函数。无论如何,要知道这是一个很好的技巧!我用Lua和Clojure编写了一个测试函数;在我的小Android掌上电脑上运行Lua one

Lua版本似乎工作得很好,Lua的堆栈已经有了大约300000的深度,但是有了CPS,我很容易在系统崩溃之前完成了7000000次迭代,可能是因为内存不足,而不是CPS/Lua组合的任何限制

Clojure的尝试没有那么成功。在1000次迭代中,它抱怨堆栈被破坏了,它可以用普通迭代做得更好,普通迭代的堆栈约为1600,iirc

你知道有什么问题吗?JVM固有的一些东西,或者只是一些愚蠢的noob错误?(哦,顺便说一句,之所以选择测试函数sigma(log),是因为它增长缓慢,而且Lua在Android上不支持bignums)

欢迎提出所有想法、提示和建议

Clojure代码:

user=> (defn cps2 [op]
  #_=>   (fn [a b k] (k (op a b))))
#'user/cps2

user=> (defn cps-sigma [n k]
  #_=>  ((cps2 =) n 1 (fn [b]
  #_=>           (if b                    ; growing continuation
  #_=>               (k 0)                ; in the recursive call
  #_=>               ((cps2 -) n 1 (fn [nm1]
  #_=>                        (cps-sigma nm1 (fn [f]
  #_=>                                          ((cps2 +) (Math/log n) f k)))))))))
#'user/cps-sigma

user=> (cps-sigma 1000 identity)
5912.128178488171

user=> (cps-sigma 1500 identity)

StackOverflowError   clojure.lang.Numbers.equal (Numbers.java:216)
user=> 
===================

在做了一点实验之后,我尝试了我在下面第三条评论中提到的代码

(defn mk-cps [accept? end-value kend kont]
  (fn [n]
  ((fn [n k]
    (let [cont (fn [v] (k (kont v n)))]
      (if (accept? n)
        (k end-value)
        (recur (dec n) cont))))
    n kend)))

(def sigmaln-cps (mk-cps zero? 0 identity #(+  %1 (Math/log %2)))) 

user=> (sigmaln-cps 11819) ;; #11819 iterations first try

StackOverflowError   clojure.lang.RT.doubleCast (RT.java:1312)
这显然更好,但我还是觉得太低了。从技术上讲,它应该只受内存的限制,是吗

我的意思是,玩具安卓平板电脑上的玩具Lua系统有超过7000000个…

Clojure的功能可以消除这个问题中涉及的许多令人困惑的管道:

(defn sigma [n]
  (letfn [(sig [curr n]
            (if (<= n 1)
              curr
              #(sig (+ curr (Math/log n)) (dec n))))]
    (trampoline sig 0 n)))

(sigma 1000)
=> 5912.128178488164
(sigma 1500)
=> 9474.406184917756
(sigma 1e7) ;; might take a few seconds
=> 1.511809654875759E8
(定义西格玛[n]
(letfn[(sig[当前n]
(如果(5912.128178488164
(西格玛1500)
=> 9474.406184917756
(西格玛1e7);可能需要几秒钟
=>1.511809654875759E8
传递给蹦床的函数可以返回一个新函数,在这种情况下蹦床继续“反弹”,或者返回一个非函数值,该值将是“最终值”值。此示例不涉及相互递归函数,但也可以使用
trampoline

使用这些函数,Clojure的函数可以消除此问题中涉及的许多令人困惑的管道:

(defn sigma [n]
  (letfn [(sig [curr n]
            (if (<= n 1)
              curr
              #(sig (+ curr (Math/log n)) (dec n))))]
    (trampoline sig 0 n)))

(sigma 1000)
=> 5912.128178488164
(sigma 1500)
=> 9474.406184917756
(sigma 1e7) ;; might take a few seconds
=> 1.511809654875759E8
(定义西格玛[n]
(letfn[(sig[当前n]
(如果(5912.128178488164
(西格玛1500)
=> 9474.406184917756
(西格玛1e7);可能需要几秒钟
=>1.511809654875759E8

传递给蹦床的函数可以返回一个新函数,在这种情况下蹦床继续“反弹”,或者返回一个非函数值,该值将是“最终值”值。此示例不涉及相互递归函数,但使用
trampoline

Clojure也可以实现这些函数。Clojure没有优化尾部调用。我认为您需要
recur
loop
recur
。这里的更多信息:很好。这里有一点时间。我认为,因为我正在寻找设置它为了处理“堆”(JVM中的任何含义),应用于Clojure和humans的正常规则将不起作用。我将使用“recur”重写它。类似CPS和y-combinator的东西(我之前已经看过)在你的脑海里做一些奇怪的事情。我相信最后一切都是为了好。不知怎的,我从来没有想到TCO机制会被用来允许在不使用堆栈的情况下实现非递归调用的机制。谢谢提醒!同时,如果有人有任何上述版本是用canonical Clojure编写的,或者有任何提示,提示等。那太好了。有趣的是,我使用的算法在Lua中工作正常。Lua是尾部递归的,但据我所知,我不认为该算法是尾部递归的。如果我错了,请纠正我。我在这里找到了一套非常好的教程幻灯片:基本上它涵盖了我的大部分问题。建议任何人研究这个问题。Clojure尾部调用不是经过优化的。我认为您需要
recur
loop
recur
。这里的更多信息:很好的一点。这里有一点时间。我想既然我想将其设置为在“堆”(JVM中的任何含义)上工作,即适用于Clojure和人类的正常规则将不起作用。我将使用“重现”来重写它。类似于CPS和y-combinator的东西(我之前已经看过)在你的脑海里做一些奇怪的事情。我相信最后一切都是为了好。不知怎的,我从来没有想到TCO机制会被用来允许在不使用堆栈的情况下实现非递归调用的机制。谢谢提醒!同时,如果有人有任何上述版本是用canonical Clojure编写的,或者有任何提示,提示等。那太好了。有趣的是,我使用的算法在Lua中工作正常。Lua是尾部递归的,但据我所知,我不认为该算法是尾部递归的。如果我错了,请纠正我。我在这里找到了一组非常好的教程幻灯片:基本上它涵盖了我的大部分问题。建议任何人研究此问题。用户=>(sigma 100000000)1.7420680845246599E9在20多秒钟内出现在我那摇摇晃晃的旧桌面上。更像是这样,我知道这是可行的。我现在必须让法医检查,看看其余的人有什么问题。再次感谢Taylor!user=>(sigma 100000000)1.7420680845246599E9在我那摇摇晃晃的旧桌面上20多秒钟。更像是这样,我知道这是可行的。我现在必须让法医检查,看看其余的有什么问题。再次谢谢你,泰勒!