F# 使用continuation将二进制递归转换为尾部递归

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(

当我阅读编程手册时,我在第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(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)))
这与前面的函数基本相同,但它实际上调用了
线性化
,而不是构建包含将调用
线性化
的函数的
步骤