有效地将元素添加到嵌套Map

时间:2016-04-16 10:32:05

标签: f#

我有一个嵌套的Map,如下所示:

Map<int, Map<int, int>>

我希望能够尽可能高效,整齐地为嵌套Map添加元素。

我目前的解决方案是这样的:

let AddStuff (collection:Map<int, Map<int, int>>) firstID secondID value = 
    let newValue = collection.[firstID].Add(secondID, value)
    let newCollection = collection.Add(firstID, newValue)
    newCollection

这对我来说似乎不是F#做事的方式,所以想知道添加到嵌套地图的最佳方式是什么?

修改 一些预期的输入/输出:

let aMap = map[(1, map[(1, 1)])]
let firstID = 1
let secondID = 2
let value = 2

let newMap = aMap firstID secondID value

// newMap = map[(1, map[(1, 1); (2, 2)])] 

修改2

似乎partialCollection对输出没有影响,因此我已将其从函数中删除。

4 个答案:

答案 0 :(得分:1)

为了在样式上稍微更具功能性,您可以使用.Add函数(Map.add模块上的函数)调用替换那些Map方法调用,而不是调用方法调用单个Map个对象)。您可能还希望将collection函数的AddStuff参数移动到最后一个参数,以便可以更轻松地与|>运算符一起使用。然后它看起来像:

let AddStuff firstID secondID value (collection:Map<int, Map<int, int>>) = 
    let newValue = collection.[firstID] |> Map.add secondID value
    collection |> Map.add firstID newValue

你可以像以下一样使用它:

let newMap = aMap |> AddStuff firstID secondID value

由你来决定你更喜欢哪种风格,但我更喜欢自己的|>风格,因为它可以让我根据&#34来思考;这些数据是通过这些操作来实现的。

编辑:对于某些空格,该函数可能看起来更好一些:

let AddStuff firstID secondID value (collection:Map<int, Map<int, int>>) = 
    let newValue =
        collection.[firstID]
        |> Map.add secondID value
    collection |> Map.add firstID newValue

答案 1 :(得分:1)

到目前为止,您遇到的解决方案存在一个问题。使用索引器抛出不在地图中的键 - 您无法添加那些尚未在顶层地图中显示的内容。因此User之类的通话会中断。

这是一种巧妙的方法 - 首先定义一个通用的更新函数:

AddStuff aNewMap 7 11 3

然后从更新中编写您的函数:

/// Update a value for key if it exists,
/// otherwise return a new map with that value  
let update key f maybeColl = 
    match maybeColl with
    | Some coll ->
        let maybeElem = Map.tryFind key coll
        Map.add key (f maybeElem) coll
    | None ->
        Map.ofList [key, f None]

使/// Given a two-level map, adds a value to the nested map. let add2 firstKey secondKey value coll = (Some coll) |> update firstKey ( update secondKey (fun _ -> value)) 易于组合意味着您可以轻松编写update或一个将映射嵌套映射中的值的函数。

答案 2 :(得分:0)

好吧,正如@rmunn在评论部分所说,Map.Add基本上是“添加或替换”

所以我原来的功能是:

let AddStuff (collection:Map<int, Map<int, int>>) firstID secondID value = 
    let newValue = collection.[firstID].Add(secondID, value)
    let partialCollection = collection.Remove(firstID)
    let newCollection = partialCollectioncollection.Add(firstID, newValue)
    newCollection

现在变为:

let AddStuff (collection:Map<int, Map<int, int>>) firstID secondID value = 
    let newCollection = collection.Add(firstID, collection.[firstID].Add(secondID, value))
    newCollection

答案 3 :(得分:0)

你要求效率和整洁。一种相当简洁的方法是使用镜头来更新不可变类型的属性。

module Lenses =
  // A Lens is a type that allows "focusing" on a property of a type
  //  A Lens consists of a get and set function

  // Inspired by: http://www.haskellforall.com/2012/01/haskell-for-mainstream-programmers_28.html

  //                          getter     setter
  type Lens<'T, 'V> = Lens of ('T -> 'V)*('V -> 'T -> 'T)

  let inline get  (Lens (g, _)) v     = g v
  let inline set  (Lens (_, s)) n v   = s n v

  // >-> chains two Lenses, allows focusing on property of a property of a type
  let ( >-> ) (f : Lens<'T, 'V>) (s : Lens<'V, 'U>) : Lens<'T, 'U> =
    Lens (get f >> get s, fun n v -> set f (set s n (get f v)) v)

  // fstL is a Lens that focuses on the first element in a pair
  let fstL<'T, 'U>  : Lens<'T*'U, 'T> = 
    Lens (fst, fun n v -> (n, snd v))
  // sndL is a Lens that focuses on the second element in a pair
  let sndL<'T, 'U>  : Lens<'T*'U, 'U> = 
    Lens (snd, fun n v -> (fst v, n))
  // mapzL is a Lens that focuses on an element in a map, z is the zero value if not present
  let mapzL k z     : Lens<Map<'K, 'V>, 'V> = 
    let get v = 
      match Map.tryFind k v with
      | Some e -> e
      | _ -> z
    let set = Map.add k
    Lens (get, set)
  // mapzL is a Lens that focuses on an element in a map
  let inline mapL k = mapzL k LanguagePrimitives.GenericZero

open Lenses

[<EntryPoint>]
let main argv =
  // Creates an empty map of a map
  let empty     : Map<string, Map<int, float>> = Map.empty

  // Uses Lenses to update the map
  //  Has to pass Map.empty to mapzL as Map doesn't have a Zero member
  let modified1 = empty     |> set (mapzL "A" Map.empty >-> mapL 2) 3.
  let modified2 = modified1 |> set (mapzL "B" Map.empty >-> mapL 3) 4.
  let modified3 = modified2 |> set (mapzL "B" Map.empty >-> mapL 4) 5.

  printfn "%A" empty
  printfn "%A" modified1
  printfn "%A" modified2
  printfn "%A" modified3

  0