2014-09-21 53 views
2

这个问题是关于函数式编程的。示例代码在F#中。用延续传递风格重写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)) 

这变得复杂了,但仍然是manaegable。

但问题是:如何重写下面的一段代码?

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

有没有简单的方法来重写它? (可能避免一个明确的递归函数,如“let rec ...”)谢谢

回答

6

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

在这种情况下,你需要写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返回结果。这也是f函数传递给map的情况!请参阅:

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

如果您使用延续巨资,那么它可能是更容易编写计算生成器(又名单子)与延续工作。这并不完全适合在一个单一的StackOverflow的答案,但看到这个excellent post by Brian McNamara

0

所以使用托马斯地图功能回答这个问题:

第一重写代码为

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 

      ... 
     )