2010-09-15 4 views
4

Я столкнулся с ошибкой в ​​своем коде, что заставляет меня думать, что я действительно не понимаю некоторые подробности о F # и ленивую оценку. Я знаю, что F # оценивает жадностью и поэтому я несколько озадачен следующей функции:F # lazy eval от считывателя потока?

// 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>[email protected]() 
Stopped due to error 

Это заставляет меня думать, что getStringFromFile в настоящее время оценивается лениво - так Я совершенно смущен. Я не понимаю, как F # оценивает функции.

ответ

9

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

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

+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