合并收集操作

时间:2016-10-08 21:06:06

标签: scala collections functional-programming scala-collections

请看下面的代码:

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
}

4 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:2)

热切评估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

答案 2 :(得分:1)

您可以在filter

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

如果你想要drop并且懒惰,你需要视图,迭代器或流。流版本,因为其他答案涵盖迭代器和视图;

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 :(得分:1)

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

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

事实上,理解被转化为withFilter和map / flatmap的组合。

withFilter是在使用方法链进行延迟评估时专门设计的。过滤器会收集一个集合并在现场生成一个新集合。但是,withFilter不执行任何操作,并将未经过滤的数据传递给下一个操作。因此,如果下一个操作是地图,则实现结果,并且一起评估整个过滤器+地图组合。