【问题标题】:Merge collection operations合并集合操作
【发布时间】:2017-02-17 15:24:01
【问题描述】:

看下面的代码:

val a = List(1,2,3,4,5)
a.filter(_ >= 3).map(_ * 9).drop(1).take(2)

我的理解是每次操作都会遍历列表一次,生成类似这样的代码:

for(i <- a) {
    // filter
}
for (i <- afiltered) {
   // map
}
for (i <- afilteredandmapped) {
   // drop
}
..etc

是否有组合这些操作的链接操作只在列表中迭代一次?生成如下:

for (i <- a) {
    // filter
    // map
    // drop
    // take
}

【问题讨论】:

  • 使用Stream 而不是List
  • 这是view应该做的,但它也有一些其他微妙的影响:a.view.filter(_&gt;=3).map(_*9).drop(1).take(2).toList

标签: scala collections functional-programming scala-collections


【解决方案1】:

为了理解这些情况,这些情况代表您组合操作并为您生成有效的代码。

(for (x <- a if x >= 3 ) yield (x * 9)).drop(1).take(2)

其实for理解就是翻译成withFilter和map/flatmap的组合。

withFilter 是在使用方法链来启用惰性求值时专门设计的。过滤器获取一个集合并在现场生成一个新集合。但是, withFilter 什么都不做,将未过滤的数据传递给下一个操作。因此,如果下一个操作是映射,则实现结果并一起评估整个过滤器 + 映射组合。

【讨论】:

    【解决方案2】:

    您可以在collect 中组合filtermap

    val a = List(1,2,3,4,5)
    a.collect {case x if x >= 3 => x * 9}.drop(1).take(2)
    

    如果您想让 drop 和 take 变得懒惰,您将需要视图、迭代器或流。流版本,因为其他答案涵盖迭代器和视图;

    val xs = a.toStream
    xs.filter{x=>println("filtering " + x); x >= 3}
      .map{x=>println("mapping " + x); x * 9}
      .drop(1)
      .take(2)
      .toList
    //> filtering 1
    //| filtering 2
    //| filtering 3
    //| mapping 3
    //| filtering 4
    //| mapping 4
    //| filtering 5
    //| mapping5
    //| res1: List[Int] = List(36, 45)
    

    【讨论】:

      【解决方案3】:

      Scala 集合被热切地评估。防止创建中间集合的最简单和最可靠的方法是将转换应用于迭代器而不是集合本身:

      val a = List(1,2,3,4,5)
      a.filter(_ >= 3).map(_ * 9).drop(1).take(2)
      

      在结果之外创建 3 个中间列表

      val a = List(1,2,3,4,5)
      a.iterator.filter(_ >= 3).map(_ * 9).drop(1).take(2).toList
      

      只创建结果列表。

      这与 Yuval Itzachow 的解决方案非常相似。但是视图有点复杂,因为它们试图保留集合的类型(每个集合都有一个对应的视图),所以如果你不介意说明你想要的结果是什么,那么迭代器方法是更可取的 @987654323 @ 与 force.

      【讨论】:

        【解决方案4】:

        Scala 2.8 集合引入了Views 的概念,它是严格集合的惰性对应物。它允许您在对集合应用多个转换时避免集合的中间分配。

        您可以通过在集合上调用.view 来使用它们,当您想要实现它们时,您可以通过.forcetoList/.toSeq 来实现:

        val result = a
                      .view
                      .filter(_ >= 3)
                      .map(_ * 9)
                      .drop(1)
                      .take(2)
                      .force
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-04-14
          • 1970-01-01
          • 1970-01-01
          • 2012-03-05
          • 1970-01-01
          • 1970-01-01
          • 2012-12-17
          • 2019-12-25
          相关资源
          最近更新 更多