我遇到了关于F#中“厄运金字塔”的this question。接受的答案涉及使用Active Patterns,但我的理解是它也可以使用Computation Expressions解决。
如何使用Computation Expressions从此代码中删除“厄运金字塔”?
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
答案 0 :(得分:7)
F# for fun and profit有一个针对此特定案例的示例:
type OrElseBuilder() =
member this.ReturnFrom(x) = x
member this.Combine (a,b) =
match a with
| Some _ -> a // a succeeds -- use it
| None -> b // a fails -- use b instead
member this.Delay(f) = f()
let orElse = new OrElseBuilder()
但是如果要将它与IDictionary
一起使用,则需要一个返回选项的查找函数:
let tryGetValue key (d:System.Collections.Generic.IDictionary<_,_>) =
match d.TryGetValue key with
| true, v -> Some v
| false, _ -> None
现在这是一个修改后的例子,它来自 F#的乐趣和利润:
let map1 = [ ("1","One"); ("2","Two") ] |> dict
let map2 = [ ("A","Alice"); ("B","Bob") ] |> dict
let map3 = [ ("CA","California"); ("NY","New York") ] |> dict
let multiLookup key = orElse {
return! map1 |> tryGetValue key
return! map2 |> tryGetValue key
return! map3 |> tryGetValue key
}
multiLookup "A" // Some "Alice"
答案 1 :(得分:5)
我喜欢“金字塔的厄运”去除的模式是:
1)创建一个惰性输入集合 2)用计算函数映射它们 3)跳过产生不可接受结果的所有计算 4)选择符合您标准的第一个。
但是,此方法不使用计算表达式
open System.Collections
let a = dict [1, "hello1"]
let b = dict [2, "hello2"]
let c = dict [2, "hello3"]
let valueGetter (key:'TKey) (d:Generic.IDictionary<'TKey, 'TVal>) =
(
match d.TryGetValue(key) with
| (true, v) -> Some(v)
| _ -> None
)
let dicts = Seq.ofList [a; b; c] // step 1
let computation data key =
data
|> (Seq.map (valueGetter key)) // step 2
|> Seq.skipWhile(fun x -> x = None) // step 3
|> Seq.head // step 4
computation dicts 2
答案 2 :(得分:5)
如果我们颠覆Bind
方法,可以实现短路表达式,我们可以简单地忽略其余的计算并用成功匹配替换它。此外,我们可以满足标准字典查找的bool*string
签名。
type OrElseBuilder() =
member __.Return x = x
member __.Bind(ma, f) =
match ma with
| true, v -> v
| false, _ -> f ()
let key = 2 in OrElseBuilder() {
do! dict[1, "1"].TryGetValue key
do! dict[2, "2"].TryGetValue key
do! dict[3, "3"].TryGetValue key
return "Nothing found" }
// val it : string = "2"