【问题标题】:Tail Recursive Combinations尾递归组合
【发布时间】:2015-06-06 15:19:00
【问题描述】:

我有这个代码:

    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 

它看起来是尾递归的,但我不断收到堆栈溢出。关于如何使它工作的任何想法?

【问题讨论】:

  • 你在哪里得到堆栈溢出? FSI?,单声道? - 您启用了 TCO?
  • 你能显示从这段代码生成的 IL 吗?
  • 这应该是尾递归的 - 很可能您没有启用 TCO 或者问题出在其他地方 - 顺便说一句:您开始的列表有多大?也许你应该考虑改用 seqs
  • 我在两个地方使用过它,一个是带有 ExcelDNA 的 .xll,第二个是 websharper(调试)中的服务器端函数

标签: f# tail-recursion


【解决方案1】:

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]

【讨论】:

  • 谢谢,有道理。
  • 有趣,我会测试一下。感谢您的帮助
猜你喜欢
  • 2019-11-05
  • 2016-03-21
  • 2018-03-17
  • 2011-03-28
  • 1970-01-01
  • 2017-11-11
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
相关资源
最近更新 更多