尾递归组合

时间:2015-06-06 15:19:00

标签: 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 

它看起来是尾递归的,但是我一直在用堆栈溢出。关于如何使其发挥作用的任何想法?

1 个答案:

答案 0 :(得分:4)

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)

  2. > 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

    2. > prepend2 [1;2;3;4] [5;6;7;8];; 
      val it : int list = [1; 2; 3; 4; 5; 6; 7; 8]