来自流阅读器的F#懒惰eval?

时间:2010-09-15 17:27:50

标签: f# evaluation lazy-evaluation eager

我遇到了我的代码中的一个错误,这让我觉得我并不真正理解有关F#和懒惰评估的一些细节。我知道F#急切地评估,因此我对以下函数感到有些困惑:

// Open a file, then read from it. Close the file. return the data.
let getStringFromFile =  
    File.OpenRead("c:\\eo\\raw.txt")
    |> fun s -> let r = new StreamReader(s)
                let data = r.ReadToEnd
                r.Close()
                s.Close()
                data

当我在FSI中称呼它时:

> let d = getStringFromFile();;

System.ObjectDisposedException: Cannot read from a closed TextReader.

at System.IO.__Error.ReaderClosed()
at System.IO.StreamReader.ReadToEnd()
at <StartupCode$FSI_0134>.$FSI_0134.main@()
Stopped due to error

这让我觉得getStringFromFile正在被懒惰地评估 - 所以我完全糊涂了。我没有了解F#如何评估函数。

2 个答案:

答案 0 :(得分:10)

为了快速解释发生了什么,让我们从这里开始:

let getStringFromFile =  
    File.OpenRead("c:\\eo\\raw.txt")
    |> fun s -> let r = new StreamReader(s)
                let data = r.ReadToEnd
                r.Close()
                s.Close()
                data

您可以将函数的前两行重写为:

let s = File.OpenRead(@"c:\eo\raw.txt")

接下来,您已省略此方法的括号:

            let data = r.ReadToEnd
            r.Close()
            s.Close()
            data

因此,data的类型为unit -> string。从函数返回此值时,整个结果为unit -> string。但是看看在分配变量和返回变量之间会发生什么:你关闭了流。

最终结果,当用户调用该函数时,流已经关闭,导致您在上面看到的错误。

并且不要忘记通过声明use whatever = ...而不是let whatever = ...来处置您的对象。

考虑到这一点,这是一个修复:

let getStringFromFile() =  
    use s = File.OpenRead(@"c:\eo\raw.txt")
    use r = new StreamReader(s)
    r.ReadToEnd()

答案 1 :(得分:2)

您没有从文件中读取。您将ReadToEnd实例的方法StreamReader绑定到值data,然后在调用getStringFromFile()时调用它。问题是此时流已关闭。

我认为你错过了括号,这里是正确的版本:

// Open a file, then read from it. Close the file. return the data.
let getStringFromFile =  
    File.OpenRead("c:\\eo\\raw.txt")
    |> fun s -> let r = new StreamReader(s)
                let data = r.ReadToEnd()
                r.Close()
                s.Close()
                data