具有两个条件的Scala List.filter,仅应用一次

Mau*_*abo 10 scala

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

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 }
}
Run Code Online (Sandbox Code Playgroud)

但是我不想迭代所有元素两次,所以有没有办法让我可以在这个集合上创建"泛型选择全部数字"并应用其他一些函数,这样它只会迭代一次?

dhg*_*dhg 26

如果你list变成一个惰性集合,比如a Iterator,那么你可以map在一遍中应用所有过滤操作(或其他类似的东西):

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

当您将集合转换为Iterator时.iterator,Scala将跟踪要执行的操作(此处为两个filter),但是将等待执行它们直到实际访问结果(此处,通过调用.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
Run Code Online (Sandbox Code Playgroud)

根据您想要做什么,您可能需要a Iterator,a Stream或a View.它们都是懒惰计算的(因此一次通过方面将适用),但它们的不同之处在于它们是否可以多次迭代(StreamView),或者它们是否保留计算值以供以后访问(Stream).

要真正看到这些不同的懒惰行为,尝试运行这段代码,并设置<OPERATION>要么toList,iterator,view,或toStream:

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
Run Code Online (Sandbox Code Playgroud)

以下是您将看到的行为:

  • List(或任何其他非惰性集合):每个集合都filter需要单独迭代.生成的过滤集合存储在内存中,以便每个集合都foreach可以显示它.
  • Iterator:filters和第一个foreach都是在一次迭代中完成的.第二个foreach因为Iterator消耗已经没有做任何事情.结果不存储在内存中.
  • View:两个foreach调用都会导致它们在集合上进行单次迭代以执行filters.结果不存储在内存中.
  • Stream:filters和第一个foreach都是在一次迭代中完成的.生成的过滤集合存储在内存中,以便每个集合都foreach可以显示它.


Lui*_*hys 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)
Run Code Online (Sandbox Code Playgroud)

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

这样做:

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

或者更好:

val n = if (someCondition) 3 else 5
val result = list filter (x => x % 2 == 0 && x % n == 0)
Run Code Online (Sandbox Code Playgroud)