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

seg*_*uso 3 f# ocaml functional-programming

这个问题是关于函数式编程的.示例代码在F#中.

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

let f x = 
    x + 1
Run Code Online (Sandbox Code Playgroud)

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

let f x cont =
    cont (x+1)
Run Code Online (Sandbox Code Playgroud)

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

例如,如果我有这个功能

let g x =
   let res = f x
   res + 2
Run Code Online (Sandbox Code Playgroud)

我必须重写g as

let g x cont =
    f x (fun res ->
            cont (res + 2) )
Run Code Online (Sandbox Code Playgroud)

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

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

let lmapped = [ for x in l do
                    let res = f x
                    yield res + 1 ]
if List.isEmpty lmapped then
   ...
Run Code Online (Sandbox Code Playgroud)

有没有一种简单的方法来重写它?(可能避免使用显式递归函数,例如"let rec ...")谢谢

Tom*_*cek 8

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

在这种情况下,您需要编写基于continuation的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
Run Code Online (Sandbox Code Playgroud)

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

该函数实际上只是一个map具有内部loop函数的普通函数,它递归地遍历输入列表并调用f以进行投影.除了所有函数都采用附加参数cont并通过最后调用返回结果cont.这也是f传递给map 的函数的情况!看到:

map (fun n cont -> cont (n * 2)) [1 .. 10] (printfn "%A")
Run Code Online (Sandbox Code Playgroud)

如果你大量使用continuation,那么编写计算构建器(也就是monad)来处理continuation可能更容易.这不太适合单个StackOverflow的答案,但请看Brian McNamara的这篇优秀文章