使用序列表达式时如何处理 F# 中的异常?

时间:2021-03-02 12:19:33

标签: f#

我正在尝试编写一个 F# 函数,该函数读取 CSV 文件并将其行作为可以在流水线表达式中进一步处理的字符串序列返回。该函数应处理打开和读取文件时可能出现的所有异常。这是我目前想到的:

// 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 {} 序列表达式中捕获异常。现在我想不出任何其他方法来设计这样一个函数,而不是事先将整个文件读入一个中间集合,如列表或数组,然后将该集合作为一个序列返回给调用者,从而失去惰性求值的所有好处.

有人有更好的主意吗?

2 个答案:

答案 0 :(得分:3)

这里的 try-with 处理程序没有捕获异常的原因是 seq 的主体被延迟执行。该函数返回序列而不产生异常,但随后尝试执行该序列会在调用者的上下文中产生异常。

由于 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

答案 1 :(得分:3)

Write-Output
相关问题