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

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

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的遞歸版本呢?以下是我遵循的步驟,記住:其中一些中間代碼並不能通過編譯。
首先看看我們原始的遞歸代碼:

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
第一步:

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):

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
那么,接下來讓我們使用相同的方法來遍歷樹,下面先列出樹的定義:

1 type NodeType = int 2 type BinaryTree = 3 | Nil 4 | Node of NodeType * BinaryTree * BinaryTree
最終的結果如下:

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函數:

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:

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:

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
結束了,接下來讓我們用下面的代碼進行測試吧:

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