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

Scala List.filter具有两个条件,仅应用一次,scala,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 ==

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

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 }
}
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

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

如果将
列表
变成一个惰性集合,例如
迭代器
,然后,您可以在一次过程中应用所有筛选操作(或其他操作,如
map
等):

val list = (1 to 12).toList
val doubleFiltered: List[Int] =
  list.iterator
    .filter(_ % 2 == 0)
    .filter(_ % 3 == 0)
    .toList
println(doubleFiltered)
当您使用
.Iterator
将集合转换为迭代器时,Scala将跟踪要执行的操作(这里有两个
过滤器
),但将等待执行这些操作,直到实际访问结果为止(这里通过调用
.toList

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

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 }
}
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
根据具体的操作,您可能需要一个
迭代器
、一个
、或一个
视图
。它们都是惰性计算的(因此一次通过的特性将适用),但它们在是否可以多次迭代(
Stream
View
)或是否保留计算值以供以后访问(
Stream
)等方面有所不同

要真正看到这些不同的惰性行为,请尝试运行这段代码,并将
设置为
toList
迭代器
查看
、或
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
val结果=
(1至12)。
.filter{e=>println(“filter 1:+e);e%2==0}
.filter{e=>println(“filter 2:+e);e%3==0}
结果foreach println
结果foreach println
以下是您将看到的行为:

  • 列表
    (或任何其他非惰性集合):每个
    过滤器
    都需要在集合中进行单独的迭代。生成的过滤集合存储在内存中,以便每个
    foreach
    都可以显示它
  • Iterator
    :在一次迭代中完成
    filter
    s和第一次
    foreach
    。第二个
    foreach
    不执行任何操作,因为
    迭代器已被使用。结果不会存储在内存中
  • 视图
    :两个
    foreach
    调用都会在集合上产生它们自己的单次迭代,以执行
    过滤器
    。结果不会存储在内存中
  • :在一次迭代中完成
    过滤器
    s和第一个
    foreach
    。生成的过滤集合存储在内存中,以便每个
    foreach
    都可以显示它
这不管用吗:

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

此外,FYI
e%2==0
将为您提供所有偶数,除非您出于其他原因命名val
赔率。

您只需在筛选器中写入两个条件:

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 }
您可以使用函数组合
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)

someCondition
不会被检查
list.size
次吗?当然,我假设它只是一个值。如果是函数调用或复杂操作,则可以在筛选操作之前将结果设置为val。仅当语句的第一部分
e%2==0
为真时,才会对其进行检查。我会将条件提取为类似
val n=If(condition)3 else 5;list.filter(e=>e%2==0&&e%n==0)
(当然,只有在条件稳定的情况下才有效)在您的示例中,初始列表上只有一次迭代,其他筛选器将应用于新的筛选(较小)集合,请您澄清在我的测试中要实现什么,只有“视图”迭代一次。当我对可遍历集合调用toIterator时,它会在调用toList之前对集合进行迭代。仅使用“视图”,我看到它仅在“toList”调用之后迭代。但是+1回答得很好,谢谢。JVM的性能通常非常好,以至于您不会注意到低效的列表操作……直到您注意到;-)someList.filter(x=>otherList.contains(x.id))的迭代器版本至少快2倍。