F# - 健全性检查和选项
F# - Sanity Checks and Options
我是 F# 的新手,所以在使用 C#/Java OOP 多年后我很难改变我的心态。
我有一个事件处理程序 MyForm.SelectFile(filePath:String)
可以打开一个对话框并让您 select 读取文件。一旦文件被 selected,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())
我一直希望 selected 文件是一个有效的 zip 存档,其中包含 2 个没有扩展名的文件:"alpha" 和 "beta".
首先,有没有更好的方法来清理我的输入?
我的 if 语句很长,我相信 F# 可以提供更好的解决方案,但我真的想不通。
其次,使用 failwith
迫使我在 MyForm.SelectFile(filePath:String)
方法中处理异常,我认为 Options 可能是更好的解决方案。
如果我需要执行两个不同的连续检查(ZipFile.IsZipFile
和内容),我不知道如何使用它们,因为在这两者之间我必须实例化一个 ZipFile
.
在 C# 中,只要检查失败,我就会 return null
然后检查 return 值与 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)
首先,函数的最后一行如果写成这样会更优雅一些:
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
声明。让我们把它变成一个函数!而且我通常更喜欢名称为 "positive" 的函数,即 isValidInput
函数通常比 isInvalidInput
更有帮助。所以让我们创建一个函数来检查压缩文件是否真的有效:
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
这看起来很容易阅读,所以我们现在可以停止重构。
这段代码看起来很奇怪。对这样一段简单的代码使用序列表达式是多余的。
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>>
。我不确定这是否是个好主意,但如果不查看您的其余代码就无法判断。
我是 F# 的新手,所以在使用 C#/Java OOP 多年后我很难改变我的心态。
我有一个事件处理程序 MyForm.SelectFile(filePath:String)
可以打开一个对话框并让您 select 读取文件。一旦文件被 selected,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())
我一直希望 selected 文件是一个有效的 zip 存档,其中包含 2 个没有扩展名的文件:"alpha" 和 "beta".
首先,有没有更好的方法来清理我的输入?
我的 if 语句很长,我相信 F# 可以提供更好的解决方案,但我真的想不通。
其次,使用 failwith
迫使我在 MyForm.SelectFile(filePath:String)
方法中处理异常,我认为 Options 可能是更好的解决方案。
如果我需要执行两个不同的连续检查(ZipFile.IsZipFile
和内容),我不知道如何使用它们,因为在这两者之间我必须实例化一个 ZipFile
.
在 C# 中,只要检查失败,我就会 return null
然后检查 return 值与 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)
首先,函数的最后一行如果写成这样会更优雅一些:
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
声明。让我们把它变成一个函数!而且我通常更喜欢名称为 "positive" 的函数,即 isValidInput
函数通常比 isInvalidInput
更有帮助。所以让我们创建一个函数来检查压缩文件是否真的有效:
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
这看起来很容易阅读,所以我们现在可以停止重构。
这段代码看起来很奇怪。对这样一段简单的代码使用序列表达式是多余的。
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>>
。我不确定这是否是个好主意,但如果不查看您的其余代码就无法判断。