Scala 中流的用例

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2096876/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 01:51:54  来源:igfitidea点击:

Use-cases for Streams in Scala

scalastreamscala-collections

提问by Jesse Eichar

In Scala there is a Stream class that is very much like an iterator. The topic Difference between Iterator and Stream in Scala?offers some insights into the similarities and differences between the two.

在 Scala 中有一个非常像迭代器的 Stream 类。主题Scala的迭代器和流之间的区别是什么?提供了一些关于两者之间的异同的见解。

Seeing how to use a stream is pretty simple but I don't have very many commonuse-cases where I would use a stream instead of other artifacts.

查看如何使用流非常简单,但我没有很多常见的用例,我会使用流而不是其他工件。

The ideas I have right now:

我现在的想法:

  • If you need to make use of an infinite series. But this does not seem like a common use-case to me so it doesn't fit my criteria. (Please correct me if it is common and I just have a blind spot)
  • If you have a series of data where each element needs to be computed but that you may want to reuse several times. This is weak because I could just load it into a list which is conceptually easier to follow for a large subset of the developer population.
  • Perhaps there is a large set of data or a computationally expensive series and there is a high probability that the items you need will not require visiting all of the elements. But in this case an Iterator would be a good match unless you need to do several searches, in that case you could use a list as well even if it would be slightly less efficient.
  • There is a complex series of data that needs to be reused. Again a list could be used here. Although in this case both cases would be equally difficult to use and a Stream would be a better fit since not all elements need to be loaded. But again not that common... or is it?
  • 如果您需要使用无限级数。但这对我来说似乎不是一个常见的用例,所以它不符合我的标准。(如果很常见,我只是有一个盲点,请纠正我)
  • 如果您有一系列数据,其中每个元素都需要计算,但您可能想要多次重复使用。这很弱,因为我可以将它加载到一个列表中,这个列表在概念上更容易被大部分开发人员遵循。
  • 也许有大量数据或计算成本高的系列,并且您需要的项目很可能不需要访问所有元素。但在这种情况下,迭代器将是一个很好的匹配,除非您需要进行多次搜索,在这种情况下,即使效率稍低,您也可以使用列表。
  • 有一系列复杂的数据需要重用。这里可以再次使用列表。尽管在这种情况下,两种情况都同样难以使用,并且 Stream 更适合,因为并非所有元素都需要加载。但又不是那么常见……或者是吗?

So have I missed any big uses? Or is it a developer preference for the most part?

那么我是否错过了任何重大用途?或者它在很大程度上是开发人员的偏好?

Thanks

谢谢

回答by Daniel C. Sobral

The main difference between a Streamand an Iteratoris that the latter is mutable and "one-shot", so to speak, while the former is not. Iteratorhas a better memory footprint than Stream, but the fact that it ismutable can be inconvenient.

aStream和 an之间的主要区别在于Iterator后者是可变的,可以说是“一次性的”,而前者不是。Iterator具有比 更好的内存占用Stream,但它可变的这一事实可能会带来不便。

Take this classic prime number generator, for instance:

以这个经典的素数生成器为例:

def primeStream(s: Stream[Int]): Stream[Int] =
  Stream.cons(s.head, primeStream(s.tail filter { _ % s.head != 0 }))
val primes = primeStream(Stream.from(2))

It can be easily be written with an Iteratoras well, but an Iteratorwon't keepthe primes computed so far.

它也可以很容易地用 an 编写Iterator,但 anIterator不会保留到目前为止计算的素数。

So, one important aspect of a Streamis that you can pass it to other functions without having it duplicated first, or having to generate it again and again.

因此, a 的一个重要方面Stream是您可以将它传递给其他函数,而无需先复制它,也不必一次又一次地生成它。

As for expensive computations/infinite lists, these things can be done with Iteratoras well. Infinite lists are actually quite useful -- you just don't know it because you didn't have it, so you have seen algorithms that are more complex than strictly necessary just to deal with enforced finite sizes.

至于昂贵的计算/无限列表,这些事情也可以完成Iterator。无限列表实际上非常有用——你只是不知道它,因为你没有它,所以你看到了比处理强制有限大小更复杂的算法。

回答by Rex Kerr

In addition to Daniel's answer, keep in mind that Streamis useful for short-circuiting evaluations. For example, suppose I have a huge set of functions that take Stringand return Option[String], and I want to keep executing them until one of them works:

除了丹尼尔的回答,请记住这Stream对短路评估很有用。例如,假设我有大量的 takeString和 return函数,Option[String]我想继续执行它们,直到其中一个起作用:

val stringOps = List(
  (s:String) => if (s.length>10) Some(s.length.toString) else None ,
  (s:String) => if (s.length==0) Some("empty") else None ,
  (s:String) => if (s.indexOf(" ")>=0) Some(s.trim) else None
);

Well, I certainly don't want to execute the entirelist, and there isn't any handy method on Listthat says, "treat these as functions and execute them until one of them returns something other than None". What to do? Perhaps this:

好吧,我当然不想执行整个列表,并且没有任何方便的方法List说“将这些视为函数并执行它们,直到其中一个返回除None“以外的其他内容。该怎么办?也许这个:

def transform(input: String, ops: List[String=>Option[String]]) = {
  ops.toStream.map( _(input) ).find(_ isDefined).getOrElse(None)
}

This takes a list and treats it as a Stream(which doesn't actually evaluate anything), then defines a new Streamthat is a result of applying the functions (but that doesn't evaluate anything either yet), then searches for the first one which is defined--and here, magically, it looks back and realizes it has to apply the map, and get the right data from the original list--and then unwraps it from Option[Option[String]]to Option[String]using getOrElse.

这需要一个列表并将其视为一个Stream(它实际上不评估任何东西),然后定义一个新的Stream,它是应用函数的结果(但它也没有评估任何东西),然后搜索第一个是定义的——在这里,神奇的是,它回顾过去并意识到它必须应用地图,并从原始列表中获取正确的数据——然后将其从 解包Option[Option[String]]Option[String]using getOrElse

Here's an example:

下面是一个例子:

scala> transform("This is a really long string",stringOps)
res0: Option[String] = Some(28)

scala> transform("",stringOps)
res1: Option[String] = Some(empty)

scala> transform("  hi ",stringOps)
res2: Option[String] = Some(hi)

scala> transform("no-match",stringOps)
res3: Option[String] = None

But does it work? If we put a printlninto our functions so we can tell if they're called, we get

但它有效吗?如果我们将 aprintln放入我们的函数中以便我们可以判断它们是否被调用,我们会得到

val stringOps = List(
  (s:String) => {println("1"); if (s.length>10) Some(s.length.toString) else None },
  (s:String) => {println("2"); if (s.length==0) Some("empty") else None },
  (s:String) => {println("3"); if (s.indexOf(" ")>=0) Some(s.trim) else None }
);
// (transform is the same)

scala> transform("This is a really long string",stringOps)
1
res0: Option[String] = Some(28)

scala> transform("no-match",stringOps)                    
1
2
3
res1: Option[String] = None

(This is with Scala 2.8; 2.7's implementation will sometimes overshoot by one, unfortunately. And note that you doaccumulate a long list of Noneas your failures accrue, but presumably this is inexpensive compared to your true computation here.)

(这是 Scala 2.8;不幸的是,2.7 的实现有时会超出 1。并且请注意,随着失败的增加,您确实积累了很长的列表None,但与您在这里的真实计算相比,这可能是廉价的。)

回答by user unknown

I could imagine, that if you poll some device in real time, a Stream is more convenient.

我可以想象,如果你实时轮询某个设备,Stream 会更方便。

Think of an GPS tracker, which returns the actual position if you ask it. You can't precompute the location where you will be in 5 minutes. You might use it for a few minutes only to actualize a path in OpenStreetMap or you might use it for an expedition over six months in a desert or the rain forest.

想想一个 GPS 跟踪器,它会在您询问时返回实际位置。您无法预先计算您将在 5 分钟内到达的位置。您可能只使用它几分钟来在 OpenStreetMap 中实现一条路径,或者您可能将它用于在沙漠或热带雨林中进行超过六个月的探险。

Or a digital thermometer or other kinds of sensors which repeatedly return new data, as long as the hardware is alive and turned on - a log file filter could be another example.

或者数字温度计或其他类型的传感器,只要硬件处于活动状态并打开,就会反复返回新数据——日志文件过滤器可能是另一个例子。

回答by retronym

Streamis to Iteratoras immutable.Listis to mutable.List. Favouring immutability prevents a class of bugs, occasionally at the cost of performance.

StreamIteratorimmutable.Listmutable.List。支持不变性可以防止一类错误,有时会以性能为代价。

scalac itself isn't immune to these problems: http://article.gmane.org/gmane.comp.lang.scala.internals/2831

scalac 本身也无法避免这些问题:http://article.gmane.org/gmane.comp.lang.scala.internals/2831

As Daniel points out, favouring laziness over strictness can simplify algorithms and make it easier to compose them.

正如 Daniel 指出的那样,偏爱懒惰而不是严格可以简化算法并使它们更容易组合。