F# 改进F中的嵌套匹配#
假设我想重复进行地图查找 在C#中,我可以使用F# 改进F中的嵌套匹配#,f#,pattern-matching,F#,Pattern Matching,假设我想重复进行地图查找 在C#中,我可以使用return实现“平坦”控制流: Thing v = null; if (a.TryGetValue(key, out v)) { return v; } if (b.TryGetValue(key, out v)) { return v; } if (c.TryGetValue(key, out v)) { return v; } return defaultValue; 有点难看,但可读性很强 在我不太熟悉
return
实现“平坦”控制流:
Thing v = null;
if (a.TryGetValue(key, out v))
{
return v;
}
if (b.TryGetValue(key, out v))
{
return v;
}
if (c.TryGetValue(key, out v))
{
return v;
}
return defaultValue;
有点难看,但可读性很强
在我不太熟悉的F#中,我会使用match
表达式:
match a.TryGetValue(key) with
| (true, v) -> v
| _ ->
match b.TryGetValue(key) with
| (true, v) -> v
| _ ->
match c.TryGetValue(key) with
| (true, v) -> v
| _ -> defaultValue
这感觉是错误的-代码越来越多地嵌套在每个映射中
F#是否提供了一种“扁平化”此代码的方法 您可以稍微更改语义并预先运行所有
TryGetValue
调用。然后您只需要一个平面模式匹配,因为您可以同时对所有结果进行模式匹配,并使用or模式(使用|
编写)选择第一个成功的模式:
match a.TryGetValue(key), b.TryGetValue(key), c.TryGetValue(key) with
| (true, v), _, _
| _, (true, v), _
| _, _, (true, v) -> v
| _ -> defaultValue
这会使模式匹配变得平坦,但您可能会进行不必要的查找(这可能不是什么大问题,但值得注意的是,这是语义的变化)
另一个选项是使用活动模式-您可以定义一个参数化的活动模式,该模式在字典上匹配,将键作为输入参数并执行查找:
let (|Lookup|_|) key (d:System.Collections.Generic.IDictionary<_, _>) =
match d.TryGetValue(key) with
| true, v -> Some v
| _ -> None
F#编译器处理这一问题的方式是,它将一个接一个地运行模式,并匹配第一个成功的模式-因此,如果第一个模式成功,则只执行一次查找。您还可以声明一个TryGet函数,仅当变量
ok
为false时,才将给定函数F应用于键。如果ok
为真,我们什么也不做,只返回给定的对作为输入
let ATryGetValue key =
if key>20 then (true,2) else (false,-2);
let BTryGetValue key =
if key>10 then (true,1) else (false,-1);
let CTryGetValue key =
if key>0 then (true,0) else (false,0);
let tryGet f (ok, key) =
if not ok then
match f key with
| (true, v) -> (true, v)
| _ -> (false, key)
else
(ok,key)
let res key =
tryGet CTryGetValue (tryGet BTryGetValue (tryGet ATryGetValue (false, key)))
printfn "%A" (res 40)
当控制流变得困难时,有时有助于解决问题。假设你有这个:
let a = [ (1, "a"); (2, "b") ] |> dict
let b = [ (42, "foo"); (7, "bar") ] |> dict
let key = 8
let defaultValue = "defaultValue"
然后,下面显示了计算背后的意图:继续尝试获取一个值,如果全部失败,则使用默认值
[ a; b ]
|> Seq.tryPick (fun d -> let (s, v) = d.TryGetValue key in if s then Some v else None)
|> defaultArg <| defaultValue
[a;b]
|>Seq.tryPick(fund d->let(s,v)=d.TryGetValue如果输入s,则输入一些v,否则无)
|>defaultArg活动模式通常是避免这种末日金字塔的好方法:0
[ a; b ]
|> Seq.tryPick (fun d -> let (s, v) = d.TryGetValue key in if s then Some v else None)
|> defaultArg <| defaultValue