withFilter而不是filter

时间:2013-10-27 11:09:51

标签: scala lazy-evaluation scala-collections for-comprehension

在使用map,flatmap等函数后,使用withFilter而不是过滤器总是更高效吗?

为什么只支持map,flatmap和foreach? (预期的功能,如forall /存在)

6 个答案:

答案 0 :(得分:111)

来自the Scala docs

  

注意:c filter pc withFilter p之间的区别在于前者       创建一个新的集合,而后者只限制域的       后续的mapflatMapforeachwithFilter操作。

因此filter会获取原始集合并生成新集合,但withFilter会非严格(即懒惰地)将未经过滤的值传递给后来的map / {{1} } / flatMap调用,保存第二遍(过滤)集合。因此,当传递给这些后续方法调用时,它会更有效。

事实上,withFilter是专门为处理这些方法的链而设计的,这是理解被去除的。此处不需要其他方法(例如withFilter / forall),因此它们尚未添加到exists的{​​{1}}返回类型中。

答案 1 :(得分:7)

the excellent answer of Shadowlands之外,我想提供filterwithFilter之间差异的直观示例。

让我们考虑以下代码

val list = List(1, 2, 3)
var go = true
val result = for(i <- list; if(go)) yield {
   go = false
   i
}

大多数人希望result等于List(1)。自Scala 2.8以来就是这种情况,因为for-comprehension被翻译成

val result = list withFilter {
  case i => go
} map {
  case i => {
    go = false
    i
  }
}

正如您所看到的,翻译会将条件转换为对withFilter的调用。之前的Scala 2.8,for-comprehension被翻译成如下内容:

val r2 = list filter {
  case i => go
} map {
  case i => {
    go = false
    i
  }
}

使用filterresult的值会大不相同:List(1, 2, 3)。我们制作go标志false的事实对过滤器没有影响,因为过滤器已经完成。同样,在Scala 2.8中,使用withFilter解决了此问题。使用withFilter时,每次在map方法中访问元素时都会评估条件。

<强>参考:    - 第120页,Scala在行动(涵盖Scala 2.10),Manning Publications,Milanjan Raychaudhuri    - Odersky's thoughts about for-comprehension translation

答案 2 :(得分:1)

对于永远存在的部分:

someList.filter(conditionA).forall(conditionB)

将与(虽然有点不直观)相同

!someList.exists(conditionA && !conditionB)

类似地,.filter()。exists()可以合并为一个exist()检查吗?

答案 3 :(得分:0)

因为 forall / exists 没有实现的主要原因是用例是:

  • 你可以懒洋洋地将withFilter应用于无限流/可迭代
  • 你可以懒惰地使用另一个withFilter(一次又一次)

要实现 forall / exists ,我们需要获取所有元素,从而失去懒惰。

例如:

import scala.collection.AbstractIterator

class RandomIntIterator extends AbstractIterator[Int] {
  val rand = new java.util.Random
  def next: Int = rand.nextInt()
  def hasNext: Boolean = true
}

//rand_integers  is an infinite random integers iterator
val rand_integers = new RandomIntIterator

val rand_naturals = 
    rand_integers.withFilter(_ > 0)

val rand_even_naturals = 
    rand_naturals.withFilter(_ % 2 == 0)

println(rand_even_naturals.map(identity).take(10).toList)

//calling a second time we get
//another ten-tuple of random even naturals
println(rand_even_naturals.map(identity).take(10).toList)

请注意, ten_rand_even_naturals 仍然是迭代器。 当我们调用 toList 时,将生成随机数并在链中进行过滤

请注意, map(identity)等同于 map(i =&gt; i),此处使用它来将withFilter对象转换回原始对象类型(例如集合,流,迭代器)

答案 4 :(得分:-3)

用于收益可以是一种解决方法,例如:

for {
  e <- col;
  if e isNotEmpty
} yield e.get(0)

答案 5 :(得分:-5)

作为一种变通方法,您只能使用mapflatMap来实现其他功能。

此外,这种优化对小型集合来说毫无用处......