我有这段代码:
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
它看起来是尾递归的,但是我一直在用堆栈溢出。关于如何使其发挥作用的任何想法?
答案 0 :(得分:4)
combinations
是尾递归的。您的问题出在@
运算符上。附加一个列表会迭代整个列表,这样当你的acc
变大时,你就会得到一个SO。
您可以看到here,@
运算符不是尾递归的。非优化版本如下:let rec (@) x y = match x with [] -> y | (h::t) -> h :: (t @ y)
。
要解决此问题,有以下几种选择:
如果您不关心订单,可以编写一个尾递归方法来预先添加结果:
let rec prepend lst1 lst2 =
match lst1 with
| [] -> lst2
| head::tail -> prepend tail (head::lst2)
> prepend [1;2;3;4] [5;6;7;8];; val it : int list = [4; 3; 2; 1; 5; 6; 7; 8]
如果您关心订单,您可以编写一个方法来首先反转列表然后添加它。当然,这样做的缺点是它需要两倍的内存,因为你要分配一个额外的列表来保存原始列表的反转版本。您可以重复使用上一个函数来编写如下内容:
let prepend2 lst1 lst2 =
prepend (prepend lst1 []) lst2
> prepend2 [1;2;3;4] [5;6;7;8];; val it : int list = [1; 2; 3; 4; 5; 6; 7; 8]