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
s和第一次filter
。第二个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倍。