为了展平嵌套match
结构,你需要使用嵌套模式。这对歧视工会最有效(正如Brian所指出的那样 - 我同意设计F#代码来主要使用歧视工会是你能做的最好的事情)。
否则,如果您想使用match
(ssp发布一个示例,其中显示专门针对您的问题的活动模式)简洁地编写代码,您需要一些活动模式。但是,您可以使用以下两种可重复使用的活动模式做到这一点:
let (|TryCast|_|) a : 'res option =
match (box a) with
| :? 'res as r -> Some(r)
| _ -> None
let (|Value|) (l:Lazy<_>) = l.Value
第一种是像:?
,但它可以让你嵌套其他模式相匹配的值(这是不可能的as
)。第二个强制评估懒惰值(我想这两个都可以在F#库中声明,因为它们非常有用)。现在,你可以写:
match lazy cond.EvalBool(), lazy body.Eval() with
| Value(true), Value(TryCast((Break(scope) : ControlFlowModifier)) as e) ->
e :> obj //Break is a DU element of ControlFlowModifier
| Value(true), _ ->
next() //all other values should call next()
| _, _ -> null
编辑:罗杰在评论中指出,这个版本的代码可能不是很可读。我认为一个更好的选择是只使用TryCast
和略有不同格式化你的原代码(虽然这不完全是标准的缩进,这是正确的,F#编译器处理它精细):
match cond.EvalBool() with
| false -> null
| true ->
match body.Eval() with
| TryCast(Break(scope) as e) -> e :> obj
| _ -> next()
这可能是基于最可读的选项模式匹配,但你也可以通过KVB使用if
instad第一match
作为版本,并与TryCast
结合起来(这完全取决于个人喜好):
if cond.EvalBool() then
match body.Eval() with
| TryCast(Break(scope) as e) -> e :> obj
| _ -> next()
else null
在任何情况下,我相信TryCast
使代码更具可读性,因为您避免了一个嵌套(由于:? .. as ..
而需要其他嵌套)。
不是你的问题的答案,但是,如果你的代码看起来像这样(使用':?'和'null'),那么你可能会考虑一个更习惯的F#解决方案或包装器。 – 2012-12-16 21:18:48