合并集合操作

Merge collection operations

看下面的代码:

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
}

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

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

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

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 的解决方案非常相似。但是视图有点复杂,因为它们试图保留集合的类型(每个集合都有一个相应的视图),所以如果您不介意说明您想要的结果是什么,则迭代器方法更可取 toListforce.

您可以在 collect

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

如果你想让 drop and 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)

For Comprehension 是针对这些情况进行的,其中代表您组合操作并为您生成有效的代码。

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

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

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