爲了展平嵌套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