使用序列表达式时如何在 F# 中处理异常?
How to handle exceptions in F# when using sequence expressions?
我正在尝试编写一个 F# 函数来读取 CSV 文件,returns 它的行作为可以在流水线表达式中进一步处理的字符串序列。该函数应处理打开和读取文件时可能出现的所有异常。这是我到目前为止想出的:
// takes a filename and returns a sequence of strings
// returns empty sequence in case file could not be opened
let readFile (f : string) =
try
seq {
use r = new StreamReader(f) // if this throws, exception is not caught below
while not r.EndOfStream do
yield reader.ReadLine() // same here
}
with ex
| ex when (ex :? Exception) ->
printfn "Exception: %s" ex.Message
Seq.empty
这里的问题是 StreamReader()
和 ReadLine()
可能抛出的异常没有被异常处理程序捕获,而是未被捕获并导致程序终止。此外,似乎没有办法尝试捕获 seq {}
序列表达式中的异常。现在我想不出任何其他方法来设计这样一个函数,除了预先将整个文件读入一个中间集合,如列表或数组,然后将这个集合作为一个序列返回给调用者,从而失去惰性评估的所有好处.
有人有更好的主意吗?
此处的 try-with 处理程序未捕获异常的原因是 seq
的主体被延迟执行。 readFile
returns 序列未生成异常,但随后尝试执行该序列会在使用它的上下文中生成异常。
由于 F# 不允许您在序列表达式中使用 try-with,因此您必须在这里有点创意。您可以使用 Seq.unfold
生成这样的序列,例如:
let readFile (f: string) =
try
new StreamReader(f)
|> Seq.unfold
(fun reader ->
try
if not reader.EndOfStream then
Some(reader.ReadLine(), reader)
else
reader.Dispose()
None
with ex ->
printfn "Exception while reading line: %O" ex
reader.Dispose()
None)
with ex ->
printfn "Exception while opening the file: %O" ex
Seq.empty
也许一种不那么棘手的方法是包装 StreamReader.ReadLine
,这样它就不会抛出异常。这样你仍然可以使用 seq
表达式和 use
语句。
let readLine (reader: StreamReader) =
try
reader.ReadLine() |> Some
with ex ->
printfn "Exception while reading line: %O" ex
None
let readFile2 (f: string) =
try
let r = new StreamReader(f)
seq {
use reader = r
let mutable error = false
while not error && not reader.EndOfStream do
let nextLine = readLine reader
if nextLine.IsSome then yield nextLine.Value else error <- true
}
with ex ->
printfn "Exception while opening the file: %O" ex
Seq.empty
let readFile (f : string) =
try File.ReadLines(f)
with ex -> printfn "Exception: %s" ex.Message; Seq.empty
我正在尝试编写一个 F# 函数来读取 CSV 文件,returns 它的行作为可以在流水线表达式中进一步处理的字符串序列。该函数应处理打开和读取文件时可能出现的所有异常。这是我到目前为止想出的:
// takes a filename and returns a sequence of strings
// returns empty sequence in case file could not be opened
let readFile (f : string) =
try
seq {
use r = new StreamReader(f) // if this throws, exception is not caught below
while not r.EndOfStream do
yield reader.ReadLine() // same here
}
with ex
| ex when (ex :? Exception) ->
printfn "Exception: %s" ex.Message
Seq.empty
这里的问题是 StreamReader()
和 ReadLine()
可能抛出的异常没有被异常处理程序捕获,而是未被捕获并导致程序终止。此外,似乎没有办法尝试捕获 seq {}
序列表达式中的异常。现在我想不出任何其他方法来设计这样一个函数,除了预先将整个文件读入一个中间集合,如列表或数组,然后将这个集合作为一个序列返回给调用者,从而失去惰性评估的所有好处.
有人有更好的主意吗?
此处的 try-with 处理程序未捕获异常的原因是 seq
的主体被延迟执行。 readFile
returns 序列未生成异常,但随后尝试执行该序列会在使用它的上下文中生成异常。
由于 F# 不允许您在序列表达式中使用 try-with,因此您必须在这里有点创意。您可以使用 Seq.unfold
生成这样的序列,例如:
let readFile (f: string) =
try
new StreamReader(f)
|> Seq.unfold
(fun reader ->
try
if not reader.EndOfStream then
Some(reader.ReadLine(), reader)
else
reader.Dispose()
None
with ex ->
printfn "Exception while reading line: %O" ex
reader.Dispose()
None)
with ex ->
printfn "Exception while opening the file: %O" ex
Seq.empty
也许一种不那么棘手的方法是包装 StreamReader.ReadLine
,这样它就不会抛出异常。这样你仍然可以使用 seq
表达式和 use
语句。
let readLine (reader: StreamReader) =
try
reader.ReadLine() |> Some
with ex ->
printfn "Exception while reading line: %O" ex
None
let readFile2 (f: string) =
try
let r = new StreamReader(f)
seq {
use reader = r
let mutable error = false
while not error && not reader.EndOfStream do
let nextLine = readLine reader
if nextLine.IsSome then yield nextLine.Value else error <- true
}
with ex ->
printfn "Exception while opening the file: %O" ex
Seq.empty
let readFile (f : string) =
try File.ReadLines(f)
with ex -> printfn "Exception: %s" ex.Message; Seq.empty