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



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

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

let f x = 
    x + 1

现在(由于我不想解释的原因,与线程有关),我必须将f变成一个带有continuations:的函数

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
   ...

有没有一种简单的方法可以重写它?(可能避免了显式递归函数,如"let rec…")感谢

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

在这种情况下,您需要编写基于延续的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

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

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

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

如果您大量使用continuation,那么编写用于处理continuation的计算生成器(又名monad)可能会更容易。这不太适合单个StackOverflow的答案,但请参阅Brian McNamara 的这篇精彩文章

因此,使用Tomas的映射函数回答问题:

首先将代码重写为

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

然后我们可以用续篇重写:

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

最新更新