F # ленивый eval от читателя потока? - PullRequest
5 голосов
/ 15 сентября 2010

Я сталкиваюсь с ошибкой в ​​моем коде, которая заставляет меня думать, что я действительно не понимаю некоторые детали о 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 ]

10 голосов
/ 15 сентября 2010

Для быстрого объяснения того, что происходит, давайте начнем здесь:

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()
2 голосов
/ 15 сентября 2010

Вы не читаете из своего файла.Вы связываете метод 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
...