【问题标题】:withFilter instead of filter用过滤器代替过滤器
【发布时间】:2013-10-27 11:09:51
【问题描述】:

在之后应用 map、flatmap 等函数时,使用 withFilter 代替 filter 是否总是更高效?

为什么只支持 map、flatmap 和 foreach? (forall/exists 等预期功能)

【问题讨论】:

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


【解决方案1】:

来自the Scala docs:

注意:c filter p 和 c withFilter p 的区别在于前者 创建一个新集合,而后者仅限制 随后的map、flatMap、foreach 和withFilter 操作。

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

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

【讨论】:

  • 希望他们有一天仍然会添加这些方法。
  • @Kigyo 我认为您不应该自己使用 withFilter (除了隐含在 for 表达式中)。如果你想让地图/过滤器变得懒惰,请使用view。
  • 我明白了。 view 和 withFilter 之间的确切区别是什么?为什么for-loops 不使用视图?
  • 仅供参考,我认为Collections - Tips and Tricks 提供了出色的信息。 H5没有锚定,但你可以在链接部分搜索Don’t create temporary collections。
  • 关于 withFilter 的显式使用,Martin Odersky 本人在 Coursera 上的 Scala 课程中明确使用了它,我强烈推荐。考虑到他这样做了,它也可能让其他人也乐于这样做,尽管差异通常只有 1 个字符。例如seq.view filter p 与seq withFilter p。
【解决方案2】:

除了the excellent answer of 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(涵盖 Scala 2.10),Manning Publications,Milanjan Raychaudhuri - Odersky's thoughts about for-comprehension translation

【讨论】:

    【解决方案3】:

    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 对象转换回原始类型(例如集合、流、迭代器)

    【讨论】:

      【解决方案4】:

      对于 forall/exists 部分:

      someList.filter(conditionA).forall(conditionB)
      

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

      !someList.exists(conditionA && !conditionB)
      

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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-10-17
            • 2017-05-07
            • 2022-01-08
            • 1970-01-01
            • 2022-01-06
            • 2021-07-03
            • 1970-01-01
            • 2014-09-23
            相关资源
            最近更新 更多