我遇到了我的代码中的一个错误,这让我觉得我并不真正理解有关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#如何评估函数。
答案 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