Ответ 1
let readLines (filePath:string) = seq {
use sr = new StreamReader (filePath)
while not sr.EndOfStream do
yield sr.ReadLine ()
}
Это версия С#:
public static IEnumerable<string> ReadLinesEnumerable(string path) {
using ( var reader = new StreamReader(path) ) {
var line = reader.ReadLine();
while ( line != null ) {
yield return line;
line = reader.ReadLine();
}
}
}
Но для прямого перевода необходима изменяемая переменная.
let readLines (filePath:string) = seq {
use sr = new StreamReader (filePath)
while not sr.EndOfStream do
yield sr.ReadLine ()
}
Если вы используете .NET 4.0, вы можете просто использовать File.ReadLines.
> let readLines filePath = System.IO.File.ReadLines(filePath);;
val readLines : string -> seq<string>
Чтобы ответить на вопрос, есть ли библиотечная функция для инкапсуляции этого шаблона - для этого нет функции, но есть функция, которая позволяет вам генерировать последовательность из некоторого состояния, называемого Seq.unfold
. Вы можете использовать его для реализации вышеперечисленных функций:
new StreamReader(filePath) |> Seq.unfold (fun sr ->
match sr.ReadLine() with
| null -> sr.Dispose(); None
| str -> Some(str, sr))
Значение sr
представляет считыватель потока и передается как состояние. Пока он дает вам ненулевые значения, вы можете вернуть Some
содержащий элемент для генерации и состояние (которое может измениться, если вы захотите). Когда он читает null
, мы удаляем его и возвращаем None
, чтобы закончить последовательность. Это не является прямым эквивалентом, потому что он не имеет права распоряжаться StreamReader
, когда генерируется исключение.
В этом случае я бы определенно использовал выражение последовательности (которое более элегантно и читаемо в большинстве случаев), но полезно знать, что он также может быть написан с использованием функции более высокого порядка.
let lines = File.ReadLines(path)
// To check
lines |> Seq.iter(fun x -> printfn "%s" x)
В .NET 2/3 вы можете:
let readLines filePath = File.ReadAllLines(filePath) |> Seq.cast<string>
и на .NET 4:
let readLines filePath = File.ReadLines(filePath);;
Чтобы избежать "System.ObjectDisposedException: не удается прочитать из закрытого TextReader". исключение, используйте:
let lines = seq { yield! System.IO.File.ReadLines "/path/to/file.txt" }