ocaml中的持续传递风格

时间:2017-10-26 04:50:25

标签: ocaml continuation-passing

我对这个概念有点困惑。所以我有以下功能

    let rec sumlist lst =
          match lst with
             | [] -> 0
             | (h::t) -> h + (sumlist t)

继续,可以写成

let rec cont_sumlist lst c =
match lst with
| [] -> (c 0)
| (h::t) -> cont_sumlist t (fun x -> c (h + x))

我仍然对c的含义及其作用

感到困惑

2 个答案:

答案 0 :(得分:6)

查看延续传递风格的一种方法是想象如果函数不允许返回,您将如何编写代码。您仍然可以通过在函数执行计算后使用每个函数的额外参数来说明您想要执行的操作。即,你传递一个充当"继续"整体计算。

您提供的代码完全按照这种方式编写,Style().textSize = 30是继续。即,它是调用者传递的函数,用于在函数执行预期计算后告知下一步该做什么。

延续传递风格是完全通用的,即所有计算都可以这种方式表达。而且,事实上,从普通的功能代码到持续传递风格的机械转换。

答案 1 :(得分:3)

已经给出了一般性答案,但具体来说,对于cont_sumlist

  • 如果[]我们"返回" (即提要)0 进入 c我们已经给予(0 空列表的总和) ,和

  • 如果(h::t)我们构建了cont_sumlist t的延续,那么之后 t的结果(即x)准备就绪后,它会与hh + x)合并,并进一步提供给c (h::t)给我们sumlist (h::t) = h + sumlist t

这就是表达等式 fun x -> c (h + x),但是评估链是显式的作为这些延续函数的链,每个函数都将其结果输入到它上面的延续功能;而不是隐含在基于堆栈的评估机制中。

换句话说,c ∘ (h +) = [h1; h2; h3; ...],因此当我们沿着列表c0 ∘ (h1 +) ∘ (h2 +) ∘ (h3 +) ∘ ...继续时,继续将逐步构建为0,并最终使用{{完全搜索列表时{1}};其中c0是用户提供给最顶层呼叫的最顶端延续,例如

cont_sumlist [1,2] (fun x -> x) 
= 
 (fun x -> (fun x -> (fun x -> x) (1 + x)) (2 + x)) 0
=
                     (fun x -> x) 
           (fun x ->              (1 + x)) 
 (fun x ->                                 (2 + x)) 0
=
 (1 + (2 + 0))

所以整体cont_sumlist [x; y; z; ... ; n] c变成了

 (c ∘ (x +) ∘ (y +) ∘ (z +) ∘ ... ∘ (n +) ) 0
= 
  c (x + (y + (z + .... + (n + 0) .... )))

关键的区别在于没有堆叠绕线和退绕,并且直接从右到左计算总和,在C类伪代码中给出一系列简单的步骤

    r = 0; 
    r += n; 
    .......
    r += z;
    r += y;
    r += x;
    call c(r);     // call c with r, without expecting c to return; like a jump

有人可能会说整体延续的构造类似于卷起堆栈,其应用对应于传统堆栈评估下堆栈的展开。

另一种表达方式是CPS定义了一种特殊的函数调用协议,这与通常的C类函数不同,它希望每个函数调用都返回。

CPS定义中的每个案例都可以解释为为函数提供小步骤语义转换规则:[] --> 0 ; (h::t) --> (h +)