减去 Map<'a, int> 的两个 Map
Subtract two Maps of Map<'a, int>
我有以下类型:
type Multiset<'a when 'a: comparison> = MSet of Map<'a, int>
我想为此类型声明一个减去两个 MSet 的函数。
假设我有以下两个多重集:
let f = MSet (Map.ofList [("a",1);("b",2);("c",1)])
let g = MSet (Map.ofList [("a",1);("b",3);("c",1)])
我现在尝试创建这个需要两个多重集的减法函数。
let subtract fms sms =
match fms with
| MSet fs -> match sms with
| MSet ss ->
let toList ms = Map.fold (fun keys key value -> keys @ [for i = 1 to value do yield key] ) [] ms
let fromList l = match l with
| [] -> MSet(Map.ofList [])
| x::xs -> MSet(Map.ofList (x::xs |> Seq.countBy id |> Seq.toList))
let sfList = toList fs
let ssList = toList ss
fromList (List.filter (fun n -> not (List.contains n sfList)) ssList)
如果我 运行 :
subtract f g
它 returns :
MSet (map [])
这不是我想要的。 g 比 f 多一个 b,所以我希望它 return:
MSet(map [("b", 1)])
我的实现不考虑同一键的多次出现。我不太确定如何解决这个问题,所以我得到了想要的功能?
我怀疑你只是颠倒了你的论点,仅此而已。试试 subtract g f
.
也就是说,您的解决方案似乎比需要的复杂得多。如何通过减去第二张地图中的计数,然后删除非正数来更新第一张地图中的值?
let sub (MSet a) (MSet b) =
let bCount key = match Map.tryFind key b with | Some c -> c | None -> 0
let positiveCounts, _ =
a
|> Map.map (fun key value -> value - (bCount key))
|> Map.partition (fun _ value -> value > 0)
MSet positiveCounts
此外,您的实现中的嵌套匹配项不需要存在。如果你想匹配两个参数,你可以这样做:
match fms, sms with
| MSet fs, MSet ss -> ...
但即使 that 也是矫枉过正 - 你可以只在参数声明中包含模式,就像我上面的实现一样。
至于重复键——在这种情况下,没有理由担心:两个参数都不能有重复键(因为它们都是 Map
s),并且算法永远不会产生任何.
潜在的问题,在您的 中也很明显,似乎是相同密钥的统一。这需要一个等式约束并且可以很容易地被高级函数 Seq.groupBy
影响。由于比较不是绝对必要的,我建议使用字典,但这种方法也适用于地图。
给定一个类型
type MultiSet<'T> = MultiSet of System.Collections.Generic.IDictionary<'T, int>
以及映射键、求和它们的值并验证结果的助手;
let internal mapSum f =
Seq.groupBy (fun (KeyValue(k, _)) -> f k)
>> Seq.map (fun (k, kvs) -> k, Seq.sumBy (fun (KeyValue(_, v)) -> v) kvs)
>> Seq.filter (fun (_, v) -> v > 0)
>> dict
>> MultiSet
您的操作变为:
let map f (MultiSet s) =
mapSum f s
let add (MultiSet fms) (MultiSet sms) =
Seq.append fms sms
|> mapSum id
let subtract (MultiSet fms) (MultiSet sms) =
Seq.map (fun (KeyValue(k, v)) ->
System.Collections.Generic.KeyValuePair(k, -v)) sms
|> Seq.append fms
|> mapSum id
let f = MultiSet(dict["a", 1; "b", 2; "c", 1])
let g = MultiSet(dict["a", 1; "b", 3; "c", 1])
subtract f g
// val it : MultiSet<string> = MultiSet (seq [])
subtract g f
// val it : MultiSet<string> = MultiSet (seq [[b, 1] {Key = "b";
// Value = 1;}])
我有以下类型:
type Multiset<'a when 'a: comparison> = MSet of Map<'a, int>
我想为此类型声明一个减去两个 MSet 的函数。
假设我有以下两个多重集:
let f = MSet (Map.ofList [("a",1);("b",2);("c",1)])
let g = MSet (Map.ofList [("a",1);("b",3);("c",1)])
我现在尝试创建这个需要两个多重集的减法函数。
let subtract fms sms =
match fms with
| MSet fs -> match sms with
| MSet ss ->
let toList ms = Map.fold (fun keys key value -> keys @ [for i = 1 to value do yield key] ) [] ms
let fromList l = match l with
| [] -> MSet(Map.ofList [])
| x::xs -> MSet(Map.ofList (x::xs |> Seq.countBy id |> Seq.toList))
let sfList = toList fs
let ssList = toList ss
fromList (List.filter (fun n -> not (List.contains n sfList)) ssList)
如果我 运行 :
subtract f g
它 returns :
MSet (map [])
这不是我想要的。 g 比 f 多一个 b,所以我希望它 return:
MSet(map [("b", 1)])
我的实现不考虑同一键的多次出现。我不太确定如何解决这个问题,所以我得到了想要的功能?
我怀疑你只是颠倒了你的论点,仅此而已。试试 subtract g f
.
也就是说,您的解决方案似乎比需要的复杂得多。如何通过减去第二张地图中的计数,然后删除非正数来更新第一张地图中的值?
let sub (MSet a) (MSet b) =
let bCount key = match Map.tryFind key b with | Some c -> c | None -> 0
let positiveCounts, _ =
a
|> Map.map (fun key value -> value - (bCount key))
|> Map.partition (fun _ value -> value > 0)
MSet positiveCounts
此外,您的实现中的嵌套匹配项不需要存在。如果你想匹配两个参数,你可以这样做:
match fms, sms with
| MSet fs, MSet ss -> ...
但即使 that 也是矫枉过正 - 你可以只在参数声明中包含模式,就像我上面的实现一样。
至于重复键——在这种情况下,没有理由担心:两个参数都不能有重复键(因为它们都是 Map
s),并且算法永远不会产生任何.
潜在的问题,在您的 Seq.groupBy
影响。由于比较不是绝对必要的,我建议使用字典,但这种方法也适用于地图。
给定一个类型
type MultiSet<'T> = MultiSet of System.Collections.Generic.IDictionary<'T, int>
以及映射键、求和它们的值并验证结果的助手;
let internal mapSum f =
Seq.groupBy (fun (KeyValue(k, _)) -> f k)
>> Seq.map (fun (k, kvs) -> k, Seq.sumBy (fun (KeyValue(_, v)) -> v) kvs)
>> Seq.filter (fun (_, v) -> v > 0)
>> dict
>> MultiSet
您的操作变为:
let map f (MultiSet s) =
mapSum f s
let add (MultiSet fms) (MultiSet sms) =
Seq.append fms sms
|> mapSum id
let subtract (MultiSet fms) (MultiSet sms) =
Seq.map (fun (KeyValue(k, v)) ->
System.Collections.Generic.KeyValuePair(k, -v)) sms
|> Seq.append fms
|> mapSum id
let f = MultiSet(dict["a", 1; "b", 2; "c", 1])
let g = MultiSet(dict["a", 1; "b", 3; "c", 1])
subtract f g
// val it : MultiSet<string> = MultiSet (seq [])
subtract g f
// val it : MultiSet<string> = MultiSet (seq [[b, 1] {Key = "b";
// Value = 1;}])