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

标签 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# 如何评估函数的信息。

最佳答案

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

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()

关于来自流阅读器的 F# 懒惰评估?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3720141/

相关文章:

c# - 如何并排组织 F# 和 C#

f# - 如何在列表中找到出现函数最大值的值

java - 定义的参数评估顺序导致次优代码?

for-loop - 方案/ Racket 中的双括号是什么?

haskell - 在 Haskell 的 ST Monad 中高效记录字符串数据

haskell - 在这个例子中,我可以确定 IO 操作的顺序吗?

clojure - 为什么 Clojure 的反向函数返回一个非惰性序列?

f# - F#在Mono 2.10上崩溃

f# - 构造值初始化

lisp - Racket中 `parameterize'和 `let'的求值顺序是怎样的?