3

考虑以下:

val stuff = Map[String, Int]("apple" -> 5, "orange" -> 1, "banana" -> 3, "kiwi" -> 2)

val used = 1

val rest = stuff.mapValues{
  case quantity => quantity - used
}.filterNot{
  case (fruit, quantity) => quantity == 0
}

结果是

rest : scala.collection.immutable.Map[String,Int] = Map(apple -> 4, banana -> 2, kiwi -> 1)

尽管我不是 Scala 方面的专家,但我知道该语言并不懒惰(与 Haskell 不同),因此mapValues会产生一个中间Map,然后将其作为输入传递给filterNot(如果链中还有其他操作) .

如何避免这种无用的中间数据结构?

注意:我知道这个问题可以推广到其他数据结构。在这里我使用了一个Map只是因为它是我在我的真实代码中使用的数据结构(尽管还有其他数据:))

4

3 回答 3

7

您可以使用view任何集合类的方法来创建集合的视图,该视图将应用类似mapfilter惰性的方法。请参阅http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.collection.TraversableLike

于 2013-01-08T17:15:43.460 回答
3

这似乎可以解决问题:

object  ChainOpsRS
{
  val stuff = Map[String, Int]("apple" -> 5, "orange" -> 1, "banana" -> 3, "kiwi" -> 2)

  val used = 1

  val rest =
    stuff.collect {
      case (fruit, quantity) if quantity > used => (fruit, quantity - used)
    }

  def main(args: Array[String]) {
    printf("stuff=%s%n", stuff.mkString("{", ", ", "}"))
    printf(" rest=%s%n", rest.mkString("{", ", ", "}"))
  }
}

运行时,它会产生以下输出:

stuff={apple -> 5, orange -> 1, banana -> 3, kiwi -> 2}
 rest={apple -> 4, banana -> 2, kiwi -> 1}
于 2013-01-09T15:20:40.340 回答
3

除了@Kim 的回答,应该注意的是该mapValues方法实际上并不计算中间结果:mapValues返回一个 Map 的视图。这使得它不同于大多数其他方法,包括filterNot甚至map.

一个例子:

val rest = stuff.mapValues {
  case quantity =>
    println("reading quantity " + quantity)
    quantity - used
}

rest("apple")
rest("apple")

印刷:

reading quantity 5
reading quantity 5
于 2013-01-08T20:56:49.920 回答