F#中Continuation編程方式


當我接觸的F#編程越多,我用到遞歸的可能性就越大,也正是因為這樣,我時常會遇到堆棧溢出的問題,要想避免堆棧溢出問題,Continuation Style Program(CSP)是唯一的方法。以下我們列出普通的遞歸和CSP的版本代碼進行對比,在這里,關鍵的一點,該方法不會返回,因此它不會在調用的堆棧上創建元素,同時,由於會延遲計算Continuation方法,它不需要被保存在棧元素中:

View Code
1 module FunctionReturnModule =   
2    let l = [1..1000000]  
3    let rec sum l =   
4      match l with  
5      | [] -> 0  
6      | h::t -> h + sum t  
7    sum l  
View Code
 1 module CPSModule =   
 2    let l = [1..1000000]  
 3    let rec sum l cont =   
 4      match l with  
 5      | [] -> cont 0  
 6      | h::t ->   
 7        let afterSum v =   
 8          cont (h+v)  
 9        sum t afterSum  
10    sum l id 

好吧,接下來的問題是如何從普通遞歸的方法得到CSP的遞歸版本呢?以下是我遵循的步驟,記住:其中一些中間代碼並不能通過編譯
首先看看我們原始的遞歸代碼:

View Code
1 module FunctionReturnModule =   
2    let l = [1..1000000]  
3    let rec sum l =   
4      match l with  
5      | [] -> 0  
6      | h::t ->   
7        let r = sum t  
8        h + r  
9    sum l

第一步:

View Code
1  module FunctionReturnModule =   
2    let l = [1..1000000]  
3    let rec sum l cont =   
4      match l with  
5      | [] -> 0  
6      | h::t ->   
7        let r = sum t  
8        cont (h + r)  
9    sum l  

第二步:處理遞歸函數中的sum,將cont移動到afterSum中,afterSum方法獲得到參數v並將它傳遞給cont(h+v):

View Code
 1 module CPSModule =   
 2    let l = [1..1000000]  
 3    let rec sum l cont =   
 4      match l with  
 5      | [] -> cont 0  
 6      | h::t ->   
 7        let afterSum v =   
 8          cont (h+v)  
 9        sum t afterSum  
10    sum l id 

那么,接下來讓我們使用相同的方法來遍歷樹,下面先列出樹的定義:

View Code
1  type NodeType = int  
2  type BinaryTree =  
3    | Nil  
4    | Node of NodeType * BinaryTree * BinaryTree 

最終的結果如下:

View Code
 1 module TreeModule =   
 2    let rec sum tree =   
 3      match tree with  
 4      | Nil -> 0  
 5      | Node(v, l, r) ->  
 6        let sumL = sum l  
 7        let sumR = sum r  
 8        v + sumL + sumR  
 9    sum deepTree  
10  module TreeCSPModule =   
11    let rec sum tree cont =   
12      match tree with  
13      | Nil -> cont 0  
14      | Node(v, l, r) ->  
15        let afterLeft lValue =   
16          let afterRight rValue =   
17            cont (v+lValue+rValue)  
18          sum r afterRight  
19        sum l afterLeft  
20    sum deepTree id 

開始使用相同的步驟將它轉換成CSP方式:

首先切入Continuation函數:

View Code
1 module TreeModule =   
2    let rec sum tree cont =   
3      match tree with  
4      | Nil -> 0  
5      | Node(v, l, r) ->  
6        let sumL = sum l  
7        let sumR = sum r  
8        cont (v + sumL + sumR)  
9    sum deepTree

第一步:處理sumR,將cont方法移動到afterRight中並將它傳給sum r:

View Code
 1 module TreeModule =   
 2    let rec sum tree cont =   
 3      match tree with  
 4      | Nil -> 0  
 5      | Node(v, l, r) ->  
 6        let sumL = sum l  
 7        // let sumR = sum r  
 8        let afterRight rValue =    
 9          cont (v + sumL + rValue)  
10        sum r afterRight  
11    sum deepTree 

第二步:處理sumL:

View Code
 1 module TreeModule =   
 2    let rec sum tree cont =   
 3      match tree with  
 4      | Nil -> 0  
 5      | Node(v, l, r) ->  
 6        //let sumL = sum l  
 7        let afterLeft lValue =  
 8          let afterRight rValue =    
 9            cont (v + lValue + rValue)  
10          sum r afterRight  
11        sum l afterLeft  
12    sum deepTree 

結束了,接下來讓我們用下面的代碼進行測試吧:

View Code
1  let tree n =   
2    let mutable subTree = Node(1, Nil, Nil)  
3    for i=0 to n do  
4      subTree <- Node(1, subTree, Nil)  
5    subTree  
6  let deepTree = tree 1000000  


注:本文為譯文,原文來自:http://apollo13cn.blogspot.com/2012/10/f-continuation-style-programming.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM