地图合并功能的改进

时间:2011-09-21 22:09:45

标签: scala

我正在编写一个函数将两个Map合并在一起。这就是我到目前为止所做的:

def merge[K, V1, V2, V3](left: Map[K, V1], right: Map[K, V2])
    (fn: (Option[V1], Option[V2]) => V3): Map[K, V3] = {
  val r = (left.keySet ++ right.keySet) map {
    key =>
      (key -> fn(left.get(key), right.get(key)))
  }
  r.toMap
}

功能本身有效。你可以这样使用这个函数:

val m1 = Map(1 -> "one", 3 -> "three", 5 -> "five")
val m2 = Map(1 -> "I", 5 -> "V", 10 -> "X")
merge(m1, m2) { (_, _) } 
// returns: 
// Map(1 -> (Some(one),Some(I)), 
//     3 -> (Some(three),None), 
//     5 -> (Some(five),Some(V)), 
//     10 -> (None,Some(X)))

我有两个问题:

  1. 我担心.get.toMap调用的 performance 计算复杂性。任何人都可以改进实施吗?
  2. 我希望默认函数生成一对值({ (_, _) })。我无法正确地获得语法。
  3. 修改 的 虽然我最初说性能,但我的意思是计算复杂性。 我的猜测是这个函数在O(n•ln(n))时间内执行。看起来我的函数大致在O(n)中执行。可以在O(ln(n))中完成吗?

2 个答案:

答案 0 :(得分:3)

对于默认函数文字使用:

(fn: (Option[V1], Option[V2]) => V3 = 
  (x: Option[V1], y: Option[V2]) => Tuple2(x,y))

你必须使用这样的合并:merge(m1,m2)()

我想说,在对实际数据进行一些测量之前,不要担心性能。

编辑:关于性能,通过提供视图而不是构建地图,您可以以查找为代价快速“构建” - 假设我们正在处理不可变映射。因此,根据实际数据和用例,您可以为某些操作获得更好的性能,但需要权衡。

class MergedView[K, V1, V2, V3](
    left: Map[K, V1], right: Map[K, V2]
  )(fn: (Option[V1], Option[V2]) => V3 = (x: Option[V1], y: Option[V2]) => Tuple2(x,y)
  ) extends collection.DefaultMap[K, V3] {
  def get(key: K): Option[V3] = (left.get(key), right.get(key)) match {
    case (None, None) => None
    case t => Some(fn(t._1, t._2))
  }
  lazy val tuples = (left.keys ++ right.keys).map(key => key -> get(key).get)
  def iterator: Iterator[(K, V3)] = tuples.iterator
} 

val r1 = new MergedView(m1, m2)() // use parens here for second param list.

答案 1 :(得分:1)

你不应该担心get - 是的,它会创建一个包装器对象,但是做其他事情会很尴尬,除非探查器显示出问题,否则你不应该尝试。< / p>

至于toMap,是的,这可能会让你失望。您可以尝试使用breakOut

关于gettoMap的复杂性,查找和添加是不可变HashMap的有效常量时间,这是默认的Map。请参阅Performance Characteristics of Scala Collections