Scala List.filter 有两个条件,只应用一次 [英] Scala List.filter with two conditions, applied only once

查看:28
本文介绍了Scala List.filter 有两个条件,只应用一次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 将跟踪要执行的操作(这里是两个 filters),但会等待执行它们,直到实际访问结果(此处,通过调用 .toList).

When you convert the collection to an Iterator with .iterator, Scala will keep track of the operations to be performed (here, two filters), 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.它们都是惰性计算的(因此将应用单遍方面),但它们在诸如是否可以多次迭代(StreamView)或他们是否保留计算值以供以后访问(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).

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

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:filters 和第一个 foreach 都在一次迭代中完成.第二个 foreach 什么都不做,因为 Iterator 已经被消耗掉了.结果不存储在内存中.
  • View:两个 foreach 调用都会导致它们自己对集合进行单遍迭代以执行 filters.结果不存储在内存中.
  • Stream:filters 和第一个 foreach 都在一次迭代中完成.生成的过滤集合存储在内存中,以便每个 foreach 可以显示它.
  • List (or any other non-lazy collection): Each filter is requires a separate iteration through the collection. The resulting filtered collection is stored in memory so that each foreach can just display it.
  • Iterator: Both filters and the first foreach are done in a single iteration. The second foreach does nothing since the Iterator has been consumed. Results are not stored in memory.
  • View: Both foreach calls result in their own single-pass iteration over the collection to perform the filters. Results are not stored in memory.
  • Stream: Both filters and the first foreach are done in a single iteration. The resulting filtered collection is stored in memory so that each foreach can just display it.

这篇关于Scala List.filter 有两个条件,只应用一次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆