我正在开发一个基本的 2D CAD 引擎,管道操作符显着改进了我的代码。基本上,有几个函数从空间中的点 (x,y) 开始,并在多次移动操作后计算最终位置:
let finalPosition =
startingPosition
|> moveByLengthAndAngle x1 a1
|> moveByXandY x2 y2
|> moveByXandAngle x3 a3
|> moveByLengthAndAngle x4 a4
// etc...
这非常容易阅读,我想保持这种状态。各种x1、a1等显然在真实代码中都有一个有意义的名称。
现在新的要求是引入异常处理。围绕整个操作链进行大量尝试/操作是不够的,因为我想知道哪一行导致了异常。我需要知道哪个参数无效,以便用户知道必须更改哪个参数。
例如,如果第一行 (moveByLengthAndAngle x1 a1) 引发异常,我想告诉类似“嘿,-90 是 a1 的无效值!a1 必须在 45 到 90 之间!”。鉴于可以在序列中使用许多相同类型的操作,为每个操作定义不同的异常类型是不够的(在本例中,我无法判断错误是第一次还是最后一次)。
显而易见的解决方案是将链拆分为单个 let 语句,每个语句都在其各自的 try/with 内。然而,这会让我美丽且可读的代码变得有点混乱,不再那么可读了。
有没有办法既能满足这个需求,又不牺牲当前代码的可读性和优雅性?
(注意。现在每个 moveBy 函数都会在出现错误时引发异常,但我可以自由地进行更改,例如返回一个选项、一个更大的元组或其他任何需要的东西)。
Rick 描述的解决方案仅处理评估时引发的异常论点管道中的功能。但是,它不会处理管道函数引发的异常(如所述回答你的另一个问题 https://stackoverflow.com/questions/8263115/why-does-pattern-matching-fail-on-an-exception-in-this-case).
例如,假设您有以下简单的功能:
let times2 n = n * 2
let plus a b = a + b
let fail n = failwith "inside fail"
10 // This will handle exception that happens when evaluating arguments
|> try plus (failwith "evaluating args") with _ -> 0
|> times2
|> try fail with _ -> 0 // This will not handle the exception from 'fail'!
为了解决这个问题,您可以编写一个函数,将任何其他函数包装在异常处理程序中。你的想法protect
函数将接受一个函数(例如times2
or fail
)并将返回一个新函数,该函数从管道(数字)获取输入并将其传递给函数(times2
or fail
),但会在异常处理程序中执行此操作:
let protect msg f =
fun n ->
try
f n
with _ ->
// Report error and return 0 to the pipeline (do something smarter here!)
printfn "Error %s" msg
0
现在您可以保护管道中的每个函数,它还将处理评估这些函数时发生的异常:
let n =
10 |> protect "Times" times2
|> protect "Fail" fail
|> protect "Plus" (plus 5)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)