这个问题是关于函数式编程的。示例代码位于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
...
)