【问题标题】:Subtract two Maps of Map<'a, int>减去 Map<'a, int> 的两个 Map
【发布时间】:2017-05-25 15:36:18
【问题描述】:

我有以下类型:

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 

返回:

MSet (map [])

这不是我想要的。 g 比 f 多一个 b,所以我希望它返回:

MSet(map [("b", 1)])

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

【问题讨论】:

    标签: f# f#-interactive


    【解决方案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 -> ...
    

    但即使是 也是一种矫枉过正 - 你可以只在参数声明中包含模式,就像在我上面的实现中一样。

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

    【讨论】:

    • 我从这些很棒的答案中学到了很多,再次非常感谢。
    【解决方案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;}])
    

    【讨论】:

    • 此操作不可能出现重复键。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-25
    • 1970-01-01
    • 2019-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-24
    • 1970-01-01
    相关资源
    最近更新 更多