递归序列会泄漏内存吗?

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上获得关于递归序列实现的技术解释是有帮助的.

Bri*_*ian 7

我现在在工作,所以我看的是比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(带有缓存的后备存储)'泄漏'(取决于你如何定义'泄漏'),请参阅

避免堆栈溢出(使用F#无限序列序列)

有趣的讨论IEnumerable(又名'seq')与LazyList以及消费者如何急切地使用LazyLists来"忘记"旧结果以防止某种"泄漏".)