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

Kev*_*Won 5 evaluation f# eager lazy-evaluation

我遇到了我的代码中的一个错误,这让我觉得我并不真正理解有关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
Run Code Online (Sandbox Code Playgroud)

当我在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
Run Code Online (Sandbox Code Playgroud)

这让我觉得这getStringFromFile是懒惰的评价 - 所以我完全糊涂了.我没有得到关于F#如何评估函数的信息.

Jul*_*iet 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
Run Code Online (Sandbox Code Playgroud)

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

let s = File.OpenRead(@"c:\eo\raw.txt")
Run Code Online (Sandbox Code Playgroud)

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

            let data = r.ReadToEnd
            r.Close()
            s.Close()
            data
Run Code Online (Sandbox Code Playgroud)

结果,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()
Run Code Online (Sandbox Code Playgroud)