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