F# 减去地图的两个地图<';a、 int>;

F# 减去地图的两个地图<';a、 int>;,f#,f#-interactive,F#,F# Interactive,我有以下类型: type Multiset<'a when 'a: comparison> = MSet of Map<'a, int> 我现在尝试创建这个减法函数,它需要两个多重集 let subtract fms sms = match fms with | MSet fs -> match sms with | MSet ss ->

我有以下类型:

type Multiset<'a when 'a: comparison> = MSet of Map<'a, int>
我现在尝试创建这个减法函数,它需要两个多重集

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)])

我的实现没有考虑同一个键的多次出现。我不太确定如何解决这个问题,所以我得到了想要的功能

我怀疑你只是把你的论点颠倒过来了,仅此而已。尝试
减去gf

也就是说,您的解决方案似乎比需要的复杂得多。通过减去第二个映射中的计数,然后删除非正计数,来更新第一个映射中的值,怎么样

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 -> ...
但是,即使这样也有点过分——您也可以在参数声明中包含该模式,就像在我上面的实现中一样


至于重复键-在这种情况下,没有理由担心:两个参数都不能有重复键(因为它们都是
Map
s),而且算法永远不会产生任何重复键。

在您的示例中,同样明显的基本问题似乎是相同键的统一。这需要相等约束,并且可以通过高级函数
Seq.groupBy
轻松实现。由于比较不是绝对必要的,我建议使用字典,但这种方法也适用于地图

给定类型

type MultiSet<'T> = MultiSet of System.Collections.Generic.IDictionary<'T, int>
您的业务将成为:

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;}])
let映射f(多集s)=
映射和f s
让我们添加(多组fms)(多组sms)=
序列号:附加fms sms
|>映射和id
让我们减去(多集fms)(多集sms)=
Seq.map(fun(KeyValue(k,v))->
System.Collections.Generic.KeyValuePair(k,-v))sms
|>序号:附加fms
|>映射和id
设f=MultiSet(dict[“a”,1;“b”,2;“c”,1])
设g=MultiSet(dict[“a”,1;“b”,3;“c”,1])
减去f g
//val it:MultiSet=MultiSet(序列[])
减去gf
//valit:MultiSet=MultiSet(seq[[b,1]{Key=“b”;
//值=1;}])

我从这些精彩的答案中学到了很多,再次感谢您。对于此操作,不可能出现重复的键。
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;}])