从 F# 中的映射中删除全部



C#:

在 C# 中,我有这样的东西:

IImmutableDictionary<string, string> map = new Dictionary<string, string>
{
    {"K1", "V1"},
    {"K2", "V2"},
    {"K3", "V3"},
}.ToImmutableDictionary();
IEnumerable<string> keys = new[] {"K1,K3"};
map = map.RemoveRange(keys);

我假设引入了该方法ImmutableDictionary<K,V>.RemoveRange Method (IEnumerable<K>)因为它比一系列Remove(K)调用更有效。它只创建一次生成的不可变对象,而不是为每个要删除的元素keys创建一次。

F#:

F# 中实现相同目标的最佳方法是什么?我想出了这个递归解决方案:

let rec removeAll (map:Map<string, string>,  keys:list<string>) =
    match keys with
        | [] -> map
        | _ -> removeAll(map.Remove(keys |> Seq.head), keys.Tail)     

但我怀疑它是否像上面的RemoveRange一样有效。

问题:

  1. F# 中最有效的等效RemoveAll是什么?
  2. 你认为 F# 递归优化会编译成同样高效的东西吗?

Map.filter在这里很有用,并且可能会阻止创建许多中间映射,尽管要有效地将其用于许多键,您需要先将键放入一个集合中。

let removeAll keys map =
    let keySet = set keys
    map |> Map.filter (fun k _ -> k |> keySet.Contains |> not)
[ 1, 2
  3, 4
  5, 6
  7, 8 ]
|> Map
|> removeAll [1; 5]
// map [(3, 4); (7, 8)]

这足够小,可能不值得分解为函数。例如,如果您的数组不超过 10 个键,那么先从它们中创建一个集合可能会效率较低。

您当前的函数是尾递归的,因此应该在不创建多个堆栈帧方面对其进行优化,但是您可以通过在列表中使用模式匹配来更简单地编写它:

let rec removeAll (map:Map<_,_>,  keys:list<_>) =
    match keys with
        | [] -> map
        | key :: rest -> removeAll(map.Remove(key), rest)

另请注意,它可以通过删除类型注释或用 _ 替换它们的一部分来自动成为泛型,告诉编译器推断类型。

最新更新