减去两张地图地图<'a,int>

时间:2017-05-25 15:36:18

标签: f# f#-interactive

我有以下类型:

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

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

2 个答案:

答案 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;}])