我喜欢递归定义序列如下:
let rec startFrom x =
seq {
yield x;
yield! startFrom (x + 1)
}
我不知道如果这样的递归序列应该在实践中使用。 该yield!
似乎是尾递归,但我不知道100%的自正在从另一个IEnumerable的内部调用。 从我的角度来看,该代码在无需关闭它每次调用,这实际上使该功能泄漏内存以及创建的IEnumerable的实例。
将这个函数泄漏内存? 对于这个问题是它甚至“尾递归”?
[编辑添加]:我摸索周围有NProf的答案,但我认为这将有助于获得有关递归序列上,因此将实施一个技术性的解释。
我在工作中我,所以我现在正在寻找比Beta1的稍微新一点位,但在我的箱子在Release模式,然后寻找与.net反射的编译代码,看来这两个
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.3s,+/- 1秒)。 (我没有做任何内存分析,有可能我想的东西很重要。)
总之,除非你衡量和看到一个问题,我不会出汗太多思考这样的问题。
编辑
我意识到我真的不回答这个问题......我认为简单的答案是“不,不漏”。 (有一种特殊的感觉中,所有“无限” IEnumerables(带缓存的后备存储)“泄漏”(取决于你如何定义“泄漏”),见
避免堆栈溢出(与F#序列的无限序列)
为IEnumerable的对战LazyList一个有趣的讨论(又名“序列”),以及如何消费者可以消费急切地向LazyLists“忘记”老结果,以防止某一种“泄漏”。)
.NET应用程序不“泄漏”内存以这种方式。 即使你正在创建很多对象垃圾收集将释放一些没有根对应用程序本身的任何对象。
在.NET内存泄漏通常是在您使用应用程序中的非托管资源(数据库连接,内存流等)的形式。 像这样在其中创建多个对象,然后抛弃他们实例是不被认为是内存泄漏,因为垃圾收集器能够释放内存。
它不会泄露任何内存,它只会产生无限序列,但由于序列IEnumerables你可以列举他们没有记忆的担忧。 该递归序列生成函数内部发生的事实,并不影响递归的安全性。 只要记住,在调试模式下尾调用优化的可能,以允许完全调试被禁用,但在发行将没有任何问题。