在之后应用地图、平面地图等功能时,使用 withFilter 而不是 filter 总是更高效吗?
为什么只支持 map、flatmap 和 foreach?(预期的功能,如 forall/exists 以及)
在之后应用地图、平面地图等功能时,使用 withFilter 而不是 filter 总是更高效吗?
为什么只支持 map、flatmap 和 foreach?(预期的功能,如 forall/exists 以及)
来自Scala 文档:
c filter p
注意:和的区别在于c withFilter p
前者创建了一个新的集合,而后者只限制了后续map
、flatMap
、foreach
和withFilter
操作的域。
因此filter
将采用原始集合并生成一个新集合,但withFilter
将非严格(即懒惰地)将未过滤的值传递给以后的map
//调用flatMap
,withFilter
从而节省通过(过滤的)集合的第二次传递。因此,在传递到这些后续方法调用时会更有效率。
事实上,withFilter
它是专门为使用这些方法的链而设计的,这就是 for 理解被去糖化的内容。为此不需要其他方法(例如forall
/ exists
),因此它们没有添加到 . 的FilterMonadic
返回类型中withFilter
。
除了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 关于理解翻译的想法
对于 forall/exists 部分:
someList.filter(conditionA).forall(conditionB)
将与(尽管有点不直观)相同
!someList.exists(conditionA && !conditionB)
同样,.filter().exists() 可以合并为一个exists() 检查吗?
未实现forall/exists的主要原因是用例是:
要实现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 对象转换回原始类型(例如,集合、流、迭代器)
使用 for yield 可以解决,例如:
for {
e <- col;
if e isNotEmpty
} yield e.get(0)
map
作为一种解决方法,您可以仅使用和实现其他功能flatMap
。
而且,这种优化对小型集合毫无用处……</p>