F# 尾部递归组合
我有以下代码:F# 尾部递归组合,f#,tail-recursion,F#,Tail Recursion,我有以下代码: let rec combinations acc listC = match listC with | [] -> acc | h::t -> let next = [h]::List.map (fun t -> h::t) acc @ acc combinations next t 它看起来是尾部递归的,但我不断地得到堆栈溢出。关于如何让它工作,有
let rec combinations acc listC =
match listC with
| [] -> acc
| h::t ->
let next = [h]::List.map (fun t -> h::t) acc @ acc
combinations next t
它看起来是尾部递归的,但我不断地得到堆栈溢出。关于如何让它工作,有什么想法吗?
组合是尾部递归的。您的问题在于@
操作员。附加一个列表将迭代整个列表,因此当您的acc
变大时,您将得到一个so
您可以看到,@
运算符不是尾部递归的。非优化版本如下所示:let rec(@)xy=match x with[]->y |(h::t)->h::(t@y)
要解决此问题,有两种选择:
如果您不关心顺序,可以编写一个尾部递归方法来预先处理结果,如下所示:
让rec在lst1和lst2之前=
将lst1与
|[]->lst2
|head::tail->prepend tail(head::lst2)
如果您关心顺序,您可以编写一个方法,首先反转列表,然后将其前置。当然,这样做的缺点是需要两倍的内存,因为您正在分配一个额外的列表来保存原始列表的反转版本。您可以重用前面的函数编写如下内容:
let prepend2 lst1 lst2=
前置(前置lst1[])lst2
组合是尾部递归的。您的问题在于@
操作员。附加一个列表将迭代整个列表,因此当您的acc
变大时,您将得到一个so
您可以看到,@
运算符不是尾部递归的。非优化版本如下所示:let rec(@)xy=match x with[]->y |(h::t)->h::(t@y)
要解决此问题,有两种选择:
如果您不关心顺序,可以编写一个尾部递归方法来预先处理结果,如下所示:
让rec在lst1和lst2之前=
将lst1与
|[]->lst2
|head::tail->prepend tail(head::lst2)
如果您关心顺序,您可以编写一个方法,首先反转列表,然后将其前置。当然,这样做的缺点是需要两倍的内存,因为您正在分配一个额外的列表来保存原始列表的反转版本。您可以重用前面的函数编写如下内容:
let prepend2 lst1 lst2=
前置(前置lst1[])lst2
你在哪里得到堆栈溢出?单声道您启用了TCO?您可以显示从该代码生成的IL吗?这应该是尾部递归的-很可能您没有启用TCO,或者问题出在其他地方-顺便问一下:您开始的列表有多大?也许你应该考虑切换到SEQSI在两个地方使用它,一个是用ExelDeDNA作为一个.xLL,另一个是WebSaple(Debug)中的服务器端函数,你会在哪里得到堆栈溢出?单声道您启用了TCO?您可以显示从该代码生成的IL吗?这应该是尾部递归的-很可能您没有启用TCO,或者问题出在其他地方-顺便问一下:您开始的列表有多大?也许你应该考虑切换到SEQSI在两个地方使用它,一个是AxelDNA的.xLL,另一个是WebSaple(调试)中的服务器端函数,这是有意义的。有趣的是,我会测试一下。谢谢你的帮助谢谢,这很有道理。很有趣,我来测试一下。谢谢你的帮助
> prepend [1;2;3;4] [5;6;7;8];;
val it : int list = [4; 3; 2; 1; 5; 6; 7; 8]
> prepend2 [1;2;3;4] [5;6;7;8];;
val it : int list = [1; 2; 3; 4; 5; 6; 7; 8]