用连续传递样式重写f#函数

时间:2014-09-21 20:40:57

标签: f# functional-programming ocaml

这个问题是关于函数式编程的。示例代码位于F#。

假设我有一个简单的函数f:

let f x = 
    x + 1

现在(由于我不想解释,与线程相关的原因)我必须将f转换为具有延续的函数:

let f x cont =
    cont (x+1)

现在我必须重写所有调用f的函数,这些函数不再编译。

例如,如果我有这个功能

let g x =
   let res = f x
   res + 2

我必须将g重写为

let g x cont =
    f x (fun res ->
            cont (res + 2) )

这已经变得复杂了,但仍然是可以管理的。

但问题是:如何重写以下代码?

let lmapped = [ for x in l do
                    let res = f x
                    yield res + 1 ]
if List.isEmpty lmapped then
   ...

有一种简单的方法可以重写它吗? (可能避免显式的递归函数,比如"让rec ...")谢谢

2 个答案:

答案 0 :(得分:8)

使用显式延续传递样式编写代码会很快变得难看。

在这种情况下,您需要编写List.map函数的基于续度的版本:

let map f list cont = 
  let rec loop acc list cont = 
    match list with
    | [] -> cont (List.rev acc) // Reverse the list at the end & call continuation!
    | x::xs -> f x (fun x' ->   // Call `f` with `cont` that recursively calls `loop`
        loop (x'::acc) xs cont )// Call `loop` with newly projected element in `acc`
  loop [] list cont

原则上,这只是一个简单的句法转换"这可以自动完成"但是很难做到这一点而不会迷路!

该函数实际上只是一个具有内部map函数的普通loop函数,它递归迭代输入列表并调用f来进行投影。除了所有函数都采用其他参数cont并通过最后调用cont返回结果。传递给map的f函数也是如此!参见:

map (fun n cont -> cont (n * 2)) [1 .. 10] (printfn "%A")

如果您大量使用continuation,那么编写计算构建器(也就是monad)来处理continuation可能更容易。这不完全适合单个StackOverflow答案,但请参阅此excellent post by Brian McNamara

答案 1 :(得分:0)

所以使用Tomas的地图功能来回答这个问题:

首先将代码重写为

let lmapped = List.map
               (fun x ->
                   let res = f x
                   res + 1 )
               l
if List.isEmpty lmapped then
  ...

然后我们可以用continuation重写:

map
   (fun x cont ->
         f x (fun res ->
                 cont (res + 1 )))
   l
   (fun lmapped ->
        if List.isEmpty lmapped then

             ... 
        )