F# 使用continuation将二进制递归转换为尾部递归
当我阅读编程手册时,我在第195页找到了如下示例代码片段:F# 使用continuation将二进制递归转换为尾部递归,f#,functional-programming,ocaml,tail-recursion,continuations,F#,Functional Programming,Ocaml,Tail Recursion,Continuations,当我阅读编程手册时,我在第195页找到了如下示例代码片段: type ContinuationStep<'a> = | Finished | Step of 'a * (unit -> ContinuationStep<'a>) let iter f binTree = let rec linearize binTree cont = match binTree with | Empty -> cont() | Node(
type ContinuationStep<'a> =
| Finished
| Step of 'a * (unit -> ContinuationStep<'a>)
let iter f binTree =
let rec linearize binTree cont =
match binTree with
| Empty -> cont()
| Node(x, l, r) ->
Step(x, (fun () -> linearize l (fun() -> linearize r cont)))
let steps = linearize binTree (fun () -> Finished)
let rec processSteps step =
match step with
| Finished -> ()
| Step(x, getNext)
-> f x
processSteps (getNext())
processSteps steps
类型ContinuationStep ContinuationStep线性化
是尾部递归的:您不需要从递归调用返回来继续计算
fun () -> linearize l (fun() -> linearize r cont)
不调用线性化
。计算将暂停,直到processSteps
调用getNext()
该示例有点微妙,因为它不使用普通的延续,而是构建一个可以逐步计算的结构。在通常进行递归调用的地方,它返回一个值Step
,该值包含要(递归)调用的函数
在第二种情况下,线性化
函数返回一个步骤
,其中包含一个将递归调用线性化
的函数,但它不会立即进行递归调用。因此,函数不进行递归调用(它只存储递归引用)
只有在查看<代码>进程步骤时才考虑程序是否是尾递归的,因为这是实际循环的,也是尾部递归的,因为它运行< <代码>步骤>代码> <代码>步骤> >不为每个代码保持堆栈空间>步骤< /代码> .<
如果您想构造一个列表而不是一系列的惰性步骤,那么您必须在continuation中立即递归调用
线性化
:
let rec linearize binTree cont =
match binTree with
| Empty -> cont []
| Node(x, l, r) ->
linearize l (fun l -> linearize r (fun v -> cont (x::v)))
这与前面的函数基本相同,但它实际上调用了线性化
,而不是构建包含将调用线性化
的函数的步骤