5

我经常需要做类似的事情

coll.groupBy(f(_)).mapValues(_.foldLeft(x)(g(_,_)))

实现相同效果的最佳方法是什么,但要避免使用 显式构造中间集合groupBy

4

2 回答 2

4

您可以将初始集合折叠在包含中间结果的地图上:

def groupFold[A,B,X](as: Iterable[A], f: A => B, init: X, g: (X,A) => X): Map[B,X] = 
  as.foldLeft(Map[B,X]().withDefaultValue(init)){
    case (m,a) => {
      val key = f(a)
      m.updated(key, g(m(key),a))
    }
  }

你说收藏,我写Iterable,但你必须考虑在你的问题中顺序是否重要。

如果您想要高效的代码,您可能会使用 Rex 的答案中的可变映射。

于 2013-03-20T17:10:03.717 回答
3

您不能真正将其作为单行符来完成,因此在编写像这样更详细的内容之前,您应该确保需要它(因为您要求“高效”,所以从某种注重性能的角度编写):

final case class Var[A](var value: A) { }
def multifold[A,B,C](xs: Traversable[A])(f: A => B)(zero: C)(g: (C,A) => C) = {
  import scala.collection.JavaConverters._
  val m = new java.util.HashMap[B, Var[C]]
  xs.foreach{ x =>
    val v = { 
      val fx = f(x)
      val op = m.get(fx)
      if (op != null) op
      else { val nv = Var(zero); m.put(fx, nv); nv }
    }
    v.value = g(v.value, x)
  }
  m.asScala.mapValues(_.value)
}

(根据您的用例,您可能希望在最后一步中打包到不可变映射中。)这是一个实际操作示例:

scala> multifold(List("salmon","herring","haddock"))(_(0))(0)(_ + _.length)
res1: scala.collection.mutable.HashMap[Char,Int] = Map(h -> 14, s -> 6)        

现在,您可能会注意到这里有些奇怪:我使用的是 Java HashMap。这是因为 Java 的 HashMap 比 Scala 快 2-3 倍。(您可以使用 Scala HashMap 编写等效的东西,但它实际上并没有使事情比您原来的更快。)因此,此操作比您发布的快 2-3 倍。但是,除非您承受着严重的内存压力,否则创建瞬态集合实际上并不会对您造成太大伤害。

于 2013-03-20T17:12:15.660 回答