我有以下类型:
type Multiset<'a when 'a: comparison> = MSet of Map<'a, int>
我想为这种类型声明一个减去两个MSets的函数。
假设我有以下两个Multisets:
let f = MSet (Map.ofList [("a",1);("b",2);("c",1)])
let g = MSet (Map.ofList [("a",1);("b",3);("c",1)])
我现在尝试创建这个带有两个Multisets的减法函数。
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
它返回:
MSet (map [])
这不是我想要的。 g包含比f更多的b,所以我希望它返回:
MSet(map [("b", 1)])
我的实现不考虑同一个键的多次出现。我不太确定如何解决这个问题,所以我得到了想要的功能?
答案 0 :(得分:5)
我怀疑你的观点被颠倒了,就是这样。试试{
"errors": [
{
"message": "Must provide query string."
}
]
}
。
那就是说,你的解决方案似乎比它需要的更复杂。如何通过减去第二个中的计数来更新第一个地图中的值,然后删除非正数?
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 -> ...
s),并且算法永远不会产生任何键。 / p>
答案 1 :(得分:2)
基本问题,在您的other question中也很明显,似乎是相同键的统一。这需要一个等式约束,并且可以通过高级函数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;}])