正如乔恩已经说过的,您的函数不是尾递归的。基本问题是它需要多次递归调用自身(列表中的每个元素一次xs
,这是在传递给的 lambda 函数中完成的List.map
)。
如果您确实需要进行多次递归调用,则使用延续传递样式或即命令式堆栈可能是唯一的选择。延续背后的想法是,每个函数都将采用另一个函数(作为最后一个参数),当结果可用时应该执行该函数。
以下示例显示了普通版本(左侧)和基于延续(右侧)
let res = foo a b fooCont a b (fun res ->
printfn "Result: %d" res printfn "Result: %d" res)
要以延续传递风格编写函数,您还需要使用基于延续的fold
函数。您可以首先map
通过将完成的操作map
移入 lambda 函数来避免使用fold
:
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)
由于您没有发布完整的工作示例,因此我无法真正测试代码,但我认为它应该可以工作。