正如Jon所說,你的函數不是尾遞歸的。基本問題是它需要多次調用多次(xs
列表中的每個元素一次,這是在傳遞給List.map
的lambda函數中完成的)。
如果實際需要進行多次遞歸調用,則使用延續傳球風格或命令式堆疊可能是唯一的選擇。延續背後的想法是,每個函數都會接受另一個函數(作爲最後一個參數),當結果可用時應該執行該函數。
下面的例子顯示了基於普通版(左)和繼續(右側)
let res = foo a b fooCont a b (fun res ->
printfn "Result: %d" res printfn "Result: %d" res)
需要編寫一個延續傳遞風格的功能,你需要使用一個基於延續fold
功能。你可以先避免通過移動map
完成的操作成fold
lambda函數使用map
:
List.fold g acc (List.map (fun xxs -> myfunc f xxs) xs)
變爲:
List.fold (fun state xxs -> g state (myfunc f xxs)) acc xs
然後你可以重寫代碼如下所示(請注意這兩個f
和g
你沒有在你的問題中顯示現在是基於延續的函數,所以他們採取額外的參數,這代表了延續):
// The additional parameter 'cont' is the continuation to be called
// when the final result of myfunc is computed
let rec myfunc' f (T (x,xs)) cont =
if (List.isEmpty xs) then
// Call the 'f' function to process the last element and give it the
// current continuation (it will be called when 'f' calculates the result)
f x cont
else
// Using the continuation-based version of fold - the lambda now takes current
// element 'xxs', the accumulated state and a continuation to call
// when it finishes calculating
List.foldCont (fun xxs state cont ->
// Call 'myfunc' recursively - note, this is tail-call position now!
myfunc' f xxs (fun res ->
// In the continuation, we perform the aggregation using 'g'
// and the result is reported to the continuation that resumes
// folding the remaining elements of the list.
g state res cont)) acc xs cont
的List.foldCont
功能是fold
基於延續的版本,可以按如下方式寫入:
module List =
let rec foldCont f (state:'TState) (list:'T list) cont =
match list with
| [] -> cont state
| x::xs -> foldCont f state xs (fun state ->
f x state cont)
既然你沒有張貼一個完整的工作示例,我真的無法測試的代碼,但我認爲它應該工作。
難道你不需要'mapCont'或缺點在你的答案嗎? –
我花了一段時間來做一些關於延續傳遞風格的背景閱讀,並最終能夠實現我的代碼中的更改。但是,我還發現我可以更改我的代碼(在上面的問題中真正修剪了這些代碼),以便不會多次調用正文中的遞歸函數。但這裏提供的答案確實有幫助。需要使用這種風格。 – vis