Haskell:折叠地图,同时更新辅助地图

时间:2016-08-30 21:58:05

标签: haskell

请考虑以下地图:Map.fromList [(1,"foo"), (2,"bar"), (3,"foo")]

我想制作:Map.fromList [(1,"foo"), (2,"bar"), (3,"1")]。先前与“foo”相关联的最后一个键3现在与“1”相关联 - 因此通过跟随该值(并将该字符串转换为数字),我仍然可以到达“foo”。如果结果是Map.fromList [(1,"3"), (2,"bar"), (3,"foo")],那也可以。

理想情况下,我会通过折叠原始地图来实现它。与此同时,我将逐步填充带有元素(“foo”,1),(“bar”,2)等的辅助(反向)地图。如果在辅助地图中找到当前关键字,而不是插入它进入最终的地图,我会插入它的相关值。

是否有一种简单/优雅的方式来排序,没有多次传递或Monad?

main = do
     let names = Map.fromList [(1,"foo"), (2,"bar"), (3,"foo")]
         link acc k v = -- insert into map1, depending map2's lookup
         names' = Map.foldlWithKey link (Map.empty, Map.empty) names
     putStrLn $ show names'

1 个答案:

答案 0 :(得分:3)

我认为你能做的最好就是使用mapAccumWithKey。然后,这样的事情:

deduplicate :: Map Int String -> Map Int String
deduplicate = snd . Map.mapAccumWithKey go Map.empty
  where
    go accum k v = case Map.lookup v accum of
                    Nothing -> (Map.insert v (show k) accum, v)
                    Just v' -> (accum, v')

这将逐个映射键,同时穿过Map String String,跟踪已经看到的值及其对应的键。如果你想尝试一些平行的东西,显然有一些更酷的东西要做,但顺序这是最佳的 - 它应该是O(n log n),你至少需要检测共享密钥。

使用names' = deduplicate names,我得到输出fromList [(1,"foo"),(2,"bar"),(3,"1")]