F# - 理智检查和选项

时间:2016-10-05 13:50:56

标签: f# options

我对F#很陌生,所以经过多年的C#/ Java OOP后,我很难改变自己的心态。

我有一个事件处理程序MyForm.SelectFile(filePath:String),它打开一个对话框,让您选择要读取的文件。选择文件后,将调用Parser.LoadFile(filePath:String)

static member LoadFile(filePath:String) =
    if not <| ZipFile.IsZipFile(filePath) then
        failwith "invalid file specified."
    use zipFile = new ZipFile(filePath)
    if zipFile.Count <> 2 || zipFile |> Seq.exists(fun x -> x.FileName <> "alpha" && x.FileName <> "beta")  then
        failwith "invalid file specified."
    zipFile |> fun x -> Parser.Parse(x.OpenReader())

我一直希望所选文件是一个有效的zip存档,其中包含2个没有扩展名的文件:“alpha”和“beta”。

首先,有更好的方法来消毒我的输入吗?

我的if语句很长,我确信F#可以提供更好的解决方案,但我真的无法理解。

其次,使用failwith迫使我处理MyForm.SelectFile(filePath:String)方法中的异常,我认为选项可能是更好的解决方案。

如果我需要执行两个不同且连续的检查(ZipFile.IsZipFile和内容),我无法弄清楚如何使用它们,因为我需要实例化ZipFile

在C#中,只要检查失败,我就会返回null然后检查null的返回值会让我知道是否需要提示错误或继续。

当前代码:

type Parser with

    static member isValidZipFile (zipFile:ZipFile) =
        (zipFile.Count = 2) && (zipFile |> Seq.forall(fun x -> (x.FileName = "alpha") || (x.FileName = "beta")))

    static member LoadFile(filePath:String) =
        if not <| ZipFile.IsZipFile(filePath) then
            None
        else
            use zipFile = new ZipFile(filePath)
            if not <| Parser.isValidZipFile(zipFile) then
                None
            else
                Some(seq { for zipEntry in zipFile do yield Parser.Parse(zipEntry.OpenReader()) } |> Seq.toArray)

2 个答案:

答案 0 :(得分:4)

首先,如果函数的最后一行写得像:

,那么它的最后一行可能会更优雅
zipFile.OpenReader() |> Parser.Parse

其次,就你使用Option的想法而言,你是在正确的轨道上。在这种情况下,这非常简单:

static member LoadFile(filePath:String) =
    if not <| ZipFile.IsZipFile(filePath) then None else
    use zipFile = new ZipFile(filePath)
    if zipFile.Count <> 2 || zipFile |> Seq.exists(fun x -> x.FileName <> "alpha" && x.FileName <> "beta") then None else
    Some (zipFile.OpenReader() |> Parser.Parse)

最后一行也可以写成:

zipFile.OpenReader() |> Parser.Parse |> Some

现在,您提到您不喜欢长if语句。让我们把它变成一个功能!我通常更喜欢具有“正面”名称的函数,即isValidInput函数通常比isInvalidInput更有用。所以让我们创建一个函数来检查zipfile是否真的有效:

let isValid (z:ZipFile) =
    z.Count = 2 && z |> Seq.forAll(fun x -> x.FileName = "alpha" || x.FileName = "beta")

现在您的LoadFile功能可以变为:

static member LoadFile(filePath:String) =
    if not <| ZipFile.IsZipFile(filePath) then None else
    use zipFile = new ZipFile(filePath)
    if not <| isValid zipFile then None else
    zipFile.OpenReader() |> Parser.Parse |> Some

这看起来很容易阅读,所以我们现在可以停止重构。

答案 1 :(得分:2)

这段代码看起来很奇怪。对这么简单的代码使用Sequence表达式是过度的。

Some(seq { for zipEntry in zipFile do yield Parser.Parse(zipEntry.OpenReader()) } |> Seq.toArray)

你可以像这样写得更好

zipFile |> Seq.map (fun ze -> ze.OpenReader () |> Parser.parse) |> Some

或者如果你坚持在数组中这样做(为什么?)

zipFile |> Seq.map (fun ze -> ze.OpenReader () |> Parser.parse) |> Seq.toArray |> Some

您最终会获得类型签名option<seq<value>>。我不确定这是不是一个好主意,但是如果不查看其余代码就无法判断。