Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 改进F中的嵌套匹配#_F#_Pattern Matching - Fatal编程技术网

F# 改进F中的嵌套匹配#

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; 有点难看,但可读性很强 在我不太熟悉

假设我想重复进行地图查找

在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;
有点难看,但可读性很强

在我不太熟悉的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