11

不知道这是否可能,但我有一些这样的代码:

val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
val evens = list.filter { e => e % 2 == 0 }

if(someCondition) {
  val result = evens.filter { e => e % 3 == 0 }
} else {
  val result = evens.filter { e => e % 5 == 0 }
}

但是我不想对所有元素进行两次迭代,所以有没有一种方法可以创建“在这个集合上创建一个“通用的选择所有的偶数”并应用一些其他函数,这样它就只会迭代一次?

4

4 回答 4

27

如果您list变成一个惰性集合,例如 an Iterator,那么您可以一次性应用所有过滤器操作(或其他类似的操作map):

val list = (1 to 12).toList
val doubleFiltered: List[Int] =
  list.iterator
    .filter(_ % 2 == 0)
    .filter(_ % 3 == 0)
    .toList
println(doubleFiltered)

当您使用 将集合转换为 Iterator 时.iterator,Scala 将跟踪要执行的操作(这里是两个filters),但会等待执行它们直到实际访问结果(这里是通过对 的调用.toList)。

所以我可能会像这样重写你的代码:

val list = (1 to 12).toList
val evens = list.iterator.filter(_ % 2 == 0)

val result = 
  if(someCondition)
    evens.filter(_ % 3 == 0)
  else
    evens.filter(_ % 5 == 0)

result foreach println

根据您想要做什么,您可能需要一个Iterator、一个Stream或一个View。它们都是惰性计算的(因此将应用一次性方面),但它们在是否可以多次迭代(StreamView)或是否保留计算值以供以后访问(Stream)等方面有所不同。

要真正看到这些不同的惰性行为,请尝试运行这段代码并设置<OPERATION>toListiteratorviewtoStream

val result =
  (1 to 12).<OPERATION>
    .filter { e => println("filter 1: " + e); e % 2 == 0 }
    .filter { e => println("filter 2: " + e); e % 3 == 0 }
result foreach println
result foreach println

这是您将看到的行为:

  • List(或任何其他非惰性集合):每个filter都需要对集合进行单独的迭代。生成的过滤集合存储在内存中,以便每个foreach都可以显示它。
  • Iterator: filters 和 firstforeach都是在一次迭代中完成的。第二个foreach什么都不做,因为Iterator已经消耗了。结果不存储在内存中。
  • View:这两个foreach调用都会导致它们自己对集合进行单遍迭代以执行filters. 结果不存储在内存中。
  • Stream: filters 和 firstforeach都是在一次迭代中完成的。生成的过滤集合存储在内存中,以便每个foreach都可以显示它。
于 2012-08-29T17:56:19.480 回答
8

您可以使用函数组合。someCondition在决定使用哪个函数时,这里只调用一次:

def modN(n: Int)(xs: List[Int]) = xs filter (_ % n == 0)

val f = modN(2) _ andThen (if (someCondition) modN(3) else modN(5))

val result = f(list)

(这不会做你想要的 - 它仍然遍历列表两次)

只需这样做:

val f: Int => Boolean = if (someCondition) { _ % 3 == 0 } else { _ % 5 == 0 }
val result = list filter (x => x % 2 == 0 && f(x))

或者更好:

val n = if (someCondition) 3 else 5
val result = list filter (x => x % 2 == 0 && x % n == 0)
于 2012-08-29T18:38:38.810 回答
2

这行不通:

list.filter{e => e % 2 == 0 && (if (someCondition) e % 3 == 0 else e % 5 == 0)}

FYIe % 2 == 0也会给你所有的偶数,除非你odds出于其他原因命名 val。

于 2012-08-29T17:59:38.403 回答
1

您只需在过滤器中编写两个条件:

val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)

var result = List(0)
val someCondition = true

result = if (someCondition) list.filter { e => e % 2 == 0 && e % 3 == 0 }
         else               list.filter { e => e % 2 == 0 && e % 5 == 0 }
于 2012-08-29T18:00:30.927 回答