A.R*_*.R. 13 f# c#-to-f# f#-3.0
我最近一直在教自己F#,我来自命令式(C++/C#)背景.作为一个练习,我一直在研究可以用矩阵做东西的函数,比如添加,乘法,得到决定因素等等.在这方面一切都很顺利,但我发现在涉及处理时我可能没有做出最好的决定输入无效,例如:
// I want to multiply two matrices
let mult m1 m2 =
let sizeOK = validateDims m1 m2
// Here is where I am running to conceptual trouble:
// In a C# world, I would throw an exception.
if !sizeOK then
raise (InvalidOperationException("bad dimensions!")
else
doWork m1 m2
Run Code Online (Sandbox Code Playgroud)
因此,虽然这在技术上有效,但这适用于功能语言吗?它是否符合函数式编程的精神?或者将它重写为更有意义:
let mult m1 m2 =
let sizeOK = validateDims m1 m2
if !sizeOK then
None
else
Some doWork m1 m2
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我返回一个选项,它在矩阵周围添加了一个额外的层,但我也可以使用函数的结果,即使在程序中的某个稍后点的故障情况(无)和模式匹配等.那么这些类型的场景是否有最佳实践?功能程序员会做什么?
pad*_*pad 10
我倾向于避免异常,原因如下:
根据你的情况,我会按照F#核心库约定(如List.tryFind
和List.find
等),并创建两个版本:
let tryMult m1 m2 =
let sizeOK = validateDims m1 m2
if not sizeOK then
None
else
Some <| doWork m1 m2
let mult m1 m2 =
let sizeOK = validateDims m1 m2
if not sizeOK then
raise <| InvalidOperationException("bad dimensions!")
else
doWork m1 m2
Run Code Online (Sandbox Code Playgroud)
这个例子是没有例外就够用了异常.该mult
功能包含在C#兼容性中.在C#中使用库的人没有模式匹配来轻松分解选项.
选项的一个缺点是它们没有给出函数没有产生值的原因.这里太过分了; 通常选择(或Haskell术语中的monad)更适合错误处理:
let tryMult m1 m2 =
// Assume that you need to validate input
if not (validateInput m1) || not (validateInput m2) then
Choice2Of2 <| ArgumentException("bad argument!")
elif not <| validateDims m1 m2 then
Choice2Of2 <| InvalidOperationException("bad dimensions!")
else
Choice1Of2 <| doWork m1 m2
Run Code Online (Sandbox Code Playgroud)
遗憾的是F#Core缺乏操纵Choice的高阶函数.您可以在FSharpX或ExtCore库中找到这些功能.
我喜欢上面的答案,但我想添加另一个选项。这实际上取决于结果的出乎意料程度以及继续进行是否有意义。如果这是一个罕见的事件,并且调用者可能没有计划失败,那么例外是完全值得尊重的。捕获异常的代码可能比上面许多级别,并且调用者可能不打算失败。如果操作失败确实是例行结果,则“Some/None”也可以,尽管它只提供两个选项并且无法传递结果。另一种选择是建立一个有区别的可能性联合。这迫使调用者可能匹配不同的结果,是可扩展的,并且不会强迫您使每个结果具有相同的数据类型。
例如
type MultOutcome =
| RESULT of Matrix
| DIMERROR
| FOOERROR of string
let mult a b =
if dimensionsWrong then
DIMERROR
elif somethingElseIDoNotLike then
FOOERROR("specific message")
else
DIMRESULT(a*b)
match mult x y with
| DIMERROR -> printfn "I guess I screwed up my matricies"
| FOOERROR(s) -> printfn "Operation failed with message %s" s
| DIMRESULT(r) ->
// Proceed with result r
Run Code Online (Sandbox Code Playgroud)