我有一个嵌套的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对输出没有影响,因此我已将其从函数中删除。
答案 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