我喜欢递归地定义序列如下:
let rec startFrom x =
seq {
yield x;
yield! startFrom (x + 1)
}
我不确定在实践中是否应该使用这样的递归序列。
yield!
似乎是尾递归,但我不是 100% 确定,因为它是从另一个 IEnumerable 内部调用的。从我的角度来看,代码在每次调用时都会创建一个 IEnumerable 实例而不关闭它,这实际上也会使该函数泄漏内存。这个函数会泄漏内存吗?就此而言,它甚至是“尾递归”吗?
[编辑添加]:我正在用 NProf 摸索答案,但我认为获得关于在 SO 上实现递归序列的技术解释会很有帮助。
最佳答案
我现在正在工作,所以我正在查看比 Beta1 稍新的位,但是在我的机器上处于 Release 模式,然后使用 .Net Reflector 查看编译后的代码,似乎这两个
let rec startFromA x =
seq {
yield x
yield! startFromA (x + 1)
}
let startFromB x =
let z = ref x
seq {
while true do
yield !z
incr z
}
在“发布”模式下编译时生成几乎相同的 MSIL 代码。它们的运行速度与此 C# 代码的运行速度大致相同:
public class CSharpExample
{
public static IEnumerable<int> StartFrom(int x)
{
while (true)
{
yield return x;
x++;
}
}
}
(例如,我在我的盒子上运行了所有三个版本并打印了百万分之一的结果,每个版本大约需要 1.3 秒,+/- 1 秒)。 (我没有做任何内存分析;我可能遗漏了一些重要的东西。)
简而言之,除非您衡量并看到问题,否则我不会为这样的问题考虑太多。
编辑
我意识到我并没有真正回答这个问题......我认为简短的回答是“不,它不会泄漏”。 (有一种特殊的意义,其中所有“无限”IEnumerables(带有缓存的后备存储)“泄漏”(取决于您如何定义“泄漏”),请参阅
Avoiding stack overflow (with F# infinite sequences of sequences)
有关 IEnumerable(又名“seq”)与 LazyList 以及消费者如何急切地使用 LazyLists 以“忘记”旧结果以防止某种“泄漏”的有趣讨论。)
关于f# - 递归序列会泄漏内存吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1019945/