Jul*_*iet 10 f# memory-leaks tail-recursion sequence
我喜欢递归地定义序列,如下所示:
let rec startFrom x =
seq {
yield x;
yield! startFrom (x + 1)
}
Run Code Online (Sandbox Code Playgroud)
我不确定这样的递归序列是否应该在实践中使用.的yield! 出现是尾递归,但我不知道100%的自正在从另一个IEnumerable的内部调用.从我的角度来看,代码在每次调用时都会创建一个IEnumerable实例而不关闭它,这实际上会使这个函数泄漏内存.
这个功能会泄漏内存吗?就此而言,它甚至是"尾递归"?
[编辑添加]:我正在摸着NProf寻找答案,但我认为在SO上获得关于递归序列实现的技术解释是有帮助的.
我现在在工作,所以我看的是比Beta1更新的位,但是在发布模式下我的盒子然后用.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
}
Run Code Online (Sandbox Code Playgroud)
在"Release"模式下编译时生成几乎相同的MSIL代码.它们以与此C#代码大致相同的速度运行:
public class CSharpExample
{
public static IEnumerable<int> StartFrom(int x)
{
while (true)
{
yield return x;
x++;
}
}
}
Run Code Online (Sandbox Code Playgroud)
(例如,我在我的盒子上运行了所有三个版本并打印了第一百万个结果,每个版本花了大约1.3s,+/ - 1s).(我没有进行任何内存分析;我可能会遗漏一些重要内容.)
简而言之,除非你测量和看到问题,否则我不会为这样的问题想太多.
编辑
我意识到我并没有真正回答这个问题......我认为简短的回答是"不,它不泄漏".(有一种特殊意义,即所有'无限'IEnumerables(带有缓存的后备存储)'泄漏'(取决于你如何定义'泄漏'),请参阅
有趣的讨论IEnumerable(又名'seq')与LazyList以及消费者如何急切地使用LazyLists来"忘记"旧结果以防止某种"泄漏".)