它是使用递归的强大技术,因为它具有强大的可描述功能。尾递归提供比正常递归更强大的计算,因为它将递归更改为迭代。 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阶乘有什么问题?
答案 0 :(得分:11)
您需要在发布模式下编译项目或检查项目属性中的“生成尾调用”选项(如果您通过命令行运行编译器,则使用--tailcalls+
。)
默认情况下,在调试模式下未启用尾调用优化。原因是,如果启用了尾调用,您将看不到有关堆栈跟踪的有用信息。因此,默认情况下禁用它们会为您提供更愉快的调试体验(即使在调试模式下,编译器也会优化调用自身的尾递归函数,这可以处理大多数情况)。
答案 1 :(得分:2)
您可能需要将此成员添加到monad构建器:
member this.Delay(mk) = fun c -> mk () c