81

在之后应用地图、平面地图等功能时,使用 withFilter 而不是 filter 总是更高效吗?

为什么只支持 map、flatmap 和 foreach?(预期的功能,如 forall/exists 以及)

4

6 回答 6

122

来自Scala 文档

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

因此filter将采用原始集合并生成一个新集合,但withFilter将非严格(即懒惰地)将未过滤的值传递给以后的map//调用flatMapwithFilter从而节省通过(过滤的)集合的第二次传递。因此,在传递到这些后续方法调用时会更有效率。

事实上,withFilter它是专门为使用这些方法的链而设计的,这就是 for 理解被去糖化的内容。为此不需要其他方法(例如forall/ exists),因此它们没有添加到 . 的FilterMonadic返回类型中withFilter

于 2013-10-27T12:51:32.773 回答
12

除了Shadowlands的出色回答之外,我想举一个直观的例子来说明和之间的filter区别withFilter

让我们考虑以下代码

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 之前,为了理解被翻译成如下内容:

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

使用filter, 的值result将完全不同:List(1, 2, 3)go我们正在制作标志这一事实false对过滤器没有影响,因为过滤器已经完成。同样,在 Scala 2.8 中,使用withFilter. 使用时withFilter,每次在map方法内访问元素时都会评估条件。

参考: - p.120 , Scala in action (covers Scala 2.10), Manning Publications, Milanjan Raychaudhuri - Odersky 关于理解翻译的想法

于 2017-09-07T19:11:06.653 回答
1

对于 forall/exists 部分:

someList.filter(conditionA).forall(conditionB)

将与(尽管有点不直观)相同

!someList.exists(conditionA && !conditionB)

同样,.filter().exists() 可以合并为一个exists() 检查吗?

于 2019-08-06T19:47:33.977 回答
1

未实现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=>i)并且在这里使用它是为了将 withFilter 对象转换回原始类型(例如,集合、流、迭代器)

于 2018-03-30T18:21:20.713 回答
-3

使用 for yield 可以解决,例如:

for {
  e <- col;
  if e isNotEmpty
} yield e.get(0)
于 2013-10-27T11:49:03.017 回答
-5

map作为一种解决方法,您可以仅使用和实现其他功能flatMap

而且,这种优化对小型集合毫无用处……</p>

于 2013-10-27T11:16:38.773 回答