Scala List.filter 有两个条件,只应用一次 [英] Scala List.filter with two conditions, applied only once
问题描述
不知道这是否可行,但我有一些这样的代码:
Don't know if this is possible, but I have some code like this:
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 }
}
但是我不想迭代所有元素两次,所以有没有办法可以创建一个在这个集合上的通用pick-all-the-evens数字"并应用一些其他函数,以便它只迭代一次?
But I don't want to iterate over all elements twice, so is there a way that I can create a "generic pick-all-the-evens numbers on this collection" and apply some other function, so that it would only iterate once?
推荐答案
如果你把 list
变成一个惰性集合,比如一个 Iterator
,那么你就可以应用所有一次性过滤操作(或其他类似 map
等):
If you turn list
into a lazy collection, such as an Iterator
, then you can apply all the filter operations (or other things like map
etc) in one pass:
val list = (1 to 12).toList
val doubleFiltered: List[Int] =
list.iterator
.filter(_ % 2 == 0)
.filter(_ % 3 == 0)
.toList
println(doubleFiltered)
当您使用 .iterator
将集合转换为 Iterator 时,Scala 将跟踪要执行的操作(这里是两个 filter
s),但会等待执行它们,直到实际访问结果(此处,通过调用 .toList
).
When you convert the collection to an Iterator with .iterator
, Scala will keep track of the operations to be performed (here, two filter
s), but will wait to perform them until the result is actually accessed (here, via the call to .toList
).
所以我可能会像这样重写你的代码:
So I might rewrite your code like this:
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
.它们都是惰性计算的(因此将应用单遍方面),但它们在诸如是否可以多次迭代(Stream
和 View
)或他们是否保留计算值以供以后访问(Stream
).
Depending on exactly what you want to do, you might want an Iterator
, a Stream
, or a View
. They are all lazily computed (so the one-pass aspect will apply), but they differ on things like whether they can be iterated over multiple times (Stream
and View
) or whether they keep the computed value around for later access (Stream
).
要真正看到这些不同的惰性行为,请尝试运行这段代码并将
设置为 toList
、iterator
、view
或 toStream
:
To really see these different lazy behaviors, try running this bit of code and set <OPERATION>
to either toList
, iterator
, view
, or 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
以下是您将看到的行为:
Here's the behavior you will see:
List
(或任何其他非惰性集合):每个filter
都需要通过集合进行单独的迭代.生成的过滤集合存储在内存中,以便每个foreach
可以显示它.Iterator
:filter
s 和第一个foreach
都在一次迭代中完成.第二个foreach
什么都不做,因为Iterator
已经被消耗掉了.结果不存储在内存中.View
:两个foreach
调用都会导致它们自己对集合进行单遍迭代以执行filters
.结果不存储在内存中.Stream
:filter
s 和第一个foreach
都在一次迭代中完成.生成的过滤集合存储在内存中,以便每个foreach
可以显示它.
List
(or any other non-lazy collection): Eachfilter
is requires a separate iteration through the collection. The resulting filtered collection is stored in memory so that eachforeach
can just display it.Iterator
: Bothfilter
s and the firstforeach
are done in a single iteration. The secondforeach
does nothing since theIterator
has been consumed. Results are not stored in memory.View
: Bothforeach
calls result in their own single-pass iteration over the collection to perform thefilters
. Results are not stored in memory.Stream
: Bothfilter
s and the firstforeach
are done in a single iteration. The resulting filtered collection is stored in memory so that eachforeach
can just display it.
这篇关于Scala List.filter 有两个条件,只应用一次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!