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

时间:2013-09-27 02:49:37

标签: f# monads tail-recursion continuations

它是使用递归的强大技术,因为它具有强大的可描述功能。尾递归提供比正常递归更强大的计算,因为它将递归更改为迭代。 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

错误消息:“由于StackOverflowException,进程终止。”

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

2 个答案:

答案 0 :(得分:11)

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

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

答案 1 :(得分:2)

您可能需要将此成员添加到monad构建器:

member this.Delay(mk) = fun c -> mk () c