2014-09-21 52 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 

      ... 
     )