使用ContinuationMonad的100000阶乘有什么问题?

dag*_*lee 3 monads continuations f# tail-recursion

它是使用递归的强大技术,因为它具有强大的可描述功能.尾递归提供比正常递归更强大的计算,因为它将递归更改为迭代.Continuation-Passing Style(CPS)可以将大量循环代码转换为尾递归.Continuation Monad提供递归语法,但实质上它是尾递归,即迭代.它应该合理地使用Continuation Monad为100000阶乘.这是代码.

type ContinuationBuilder() =
   member b.Bind(x, f) = fun k -> x (fun x -> f x k)
   member b.Return x = fun k -> k x
   member b.ReturnFrom x = x
(*
type ContinuationBuilder =
  class
    new : unit -> ContinuationBuilder
    member Bind : x:(('d -> 'e) -> 'f) * f:('d -> 'g -> 'e) -> ('g -> 'f)
    member Return : x:'b -> (('b -> 'c) -> 'c)
    member ReturnFrom : x:'a -> 'a
  end
*)
let cont = ContinuationBuilder()
//val cont : ContinuationBuilder
let fac n =
    let rec loop n =
      cont {
              match n with
              | n when n = 0I -> return 1I   
              | _ -> let! x = fun f -> f n
                     let! y = loop (n - 1I)   
                     return x * y
           }
    loop n (fun x -> x)

let x2 = fac 100000I
Run Code Online (Sandbox Code Playgroud)

有错误的消息:"由于StackOverflowException,进程终止."

使用ContinuationMonad的100000阶乘有什么问题?

Tom*_*cek 11

您需要在发布模式下编译项目或检查项目属性中的"生成尾调用"选项(或者--tailcalls+如果您通过命令行运行编译器,则使用该选项).

默认情况下,在调试模式下未启用尾调用优化.原因是,如果启用了尾调用,您将看不到有关堆栈跟踪的有用信息.因此,默认情况下禁用它们会为您提供更愉快的调试体验(即使在调试模式下,编译器也会优化调用自身的尾递归函数,这些函数可以处理大多数情况).

  • 无论如何都需要延迟.尝试超过100000,即使在realese模式下,您也会看到堆栈溢出. (2认同)