尾递归组合

Tail Recursive Combinations

我有这个代码:

    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 

它看起来是尾递归的,但我总是遇到堆栈溢出。关于如何让它发挥作用有什么想法吗?

combinations 是尾递归。您的问题出在 @ 运算符上。用它附加一个列表会迭代整个列表,因此当您的 acc 变大时,您将得到一个 SO。

你可以看到here@运算符不是尾递归的。未优化的版本如下所示:let rec (@) x y = match x with [] -> y | (h::t) -> h :: (t @ y).

要解决这个问题,有两种选择:

  1. 如果你不关心顺序,你可以写一个尾递归方法来添加这样的结果:

    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]
  1. 如果你关心顺序,你可以写一个方法来先反转列表,然后再添加它。当然,这样做的缺点是它需要两倍的内存,因为您要分配一个额外的列表来保存原始列表的反向版本。可以复用之前的函数,写成这样:

    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]