Scala 中流的用例
Scala 中有一个 Stream 类,它非常类似于迭代器。主题 Scala 中迭代器和流之间的区别? 提供了一些见解两者之间的相同点和不同点。
了解如何使用流非常简单,但我没有很多常见用例,我会使用流而不是其他工件。
我现在的想法是:
- 如果你需要使用无限级数。但这对我来说似乎不是一个常见的用例,所以它不符合我的标准。 (如果它很常见并且我只是有一个盲点,请纠正我)
- 如果您有一系列数据,其中每个元素都需要计算,但您可能想重复使用多次。这是很弱的,因为我可以将它加载到一个列表中,这在概念上对于大部分开发人员来说更容易遵循。
- 也许有大量数据或计算成本较高的系列,并且您需要的项目很可能不需要访问所有元素。但在这种情况下,迭代器将是一个很好的匹配,除非您需要进行多次搜索,在这种情况下,您也可以使用列表,即使它的效率稍低。
- 有一系列复杂的数据需要重用。这里可以再次使用列表。尽管在这种情况下,两种情况都同样难以使用,并且 Stream 会更适合,因为并非所有元素都需要加载。但同样不常见……是吗?
那么我是否错过了任何重大用途?或者这在很大程度上是开发人员的偏好?
谢谢
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.
Seeing how to use a stream is pretty simple but I don't have very many common use-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?
So have I missed any big uses? Or is it a developer preference for the most part?
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
Stream 和 Iterator 之间的主要区别在于后者是可变的且“一次性”,可以这么说,而前者则不然。
Iterator
比Stream
具有更好的内存占用,但事实上它是可变的,这可能会带来不便。以这个经典的素数生成器为例:
它也可以很容易地用
Iterator
编写,但Iterator
不会保留到目前为止计算出的素数。因此,
Stream
的一个重要方面是您可以将其传递给其他函数,而无需先复制它,也不必一次又一次地生成它。至于昂贵的计算/无限列表,这些事情也可以使用迭代器来完成。无限列表实际上非常有用——您只是不知道它,因为您没有它,所以您已经看到了比处理强制有限大小严格必需的算法更复杂的算法。
The main difference between a
Stream
and anIterator
is that the latter is mutable and "one-shot", so to speak, while the former is not.Iterator
has a better memory footprint thanStream
, but the fact that it is mutable can be inconvenient.Take this classic prime number generator, for instance:
It can be easily be written with an
Iterator
as well, but anIterator
won't keep the primes computed so far.So, one important aspect of a
Stream
is that you can pass it to other functions without having it duplicated first, or having to generate it again and again.As for expensive computations/infinite lists, these things can be done with
Iterator
as 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.除了丹尼尔的回答之外,请记住
Stream
对于短路评估很有用。例如,假设我有一大堆函数,它们接受String
并返回Option[String]
,并且我想继续执行它们,直到其中一个起作用:嗯,我当然不想执行整个列表,并且
List
上没有任何方便的方法说:“将它们视为函数并执行它们,直到其中之一它们返回的不是None
”。该怎么办?也许是这样的:这需要一个列表并将其视为
Stream
(实际上并不评估任何内容),然后定义一个新的Stream
,它是应用函数的结果(但这还没有评估任何内容),然后搜索定义的第一个 - 在这里,它神奇地回顾并意识到它必须应用地图,并从原始列表中获取正确的数据 - - 然后使用getOrElse
将其从Option[Option[String]]
解包到Option[String]
。这是一个例子:
但这有效吗?如果我们将
println
放入我们的函数中,以便我们可以判断它们是否被调用,我们会得到(这是 Scala 2.8 的情况;不幸的是,2.7 的实现有时会超出 1。请注意,您随着失败的增加,确实会积累一长串
None
,但与此处的真实计算相比,这可能是便宜的。)In addition to Daniel's answer, keep in mind that
Stream
is useful for short-circuiting evaluations. For example, suppose I have a huge set of functions that takeString
and returnOption[String]
, and I want to keep executing them until one of them works:Well, I certainly don't want to execute the entire list, and there isn't any handy method on
List
that says, "treat these as functions and execute them until one of them returns something other thanNone
". What to do? Perhaps this:This takes a list and treats it as a
Stream
(which doesn't actually evaluate anything), then defines a newStream
that 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 fromOption[Option[String]]
toOption[String]
usinggetOrElse
.Here's an example:
But does it work? If we put a
println
into our functions so we can tell if they're called, we get(This is with Scala 2.8; 2.7's implementation will sometimes overshoot by one, unfortunately. And note that you do accumulate a long list of
None
as your failures accrue, but presumably this is inexpensive compared to your true computation here.)我可以想象,如果您实时轮询某些设备,则 Stream 会更方便。
想象一下 GPS 跟踪器,如果您询问,它会返回实际位置。您无法预先计算 5 分钟后您将到达的位置。您可能只使用它几分钟来在 OpenStreetMap 中实现一条路径,或者您可能会使用它在沙漠或雨林中进行六个月以上的探险。
或者数字温度计或其他类型的传感器,只要硬件处于活动状态并打开,就会重复返回新数据 - 日志文件过滤器可能是另一个例子。
I could imagine, that if you poll some device in real time, a Stream is more convenient.
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.
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.
Stream
之于Iterator
就像immutable.List
之于mutable.List
一样。支持不变性可以防止一类错误,有时会以性能为代价。scalac 本身也无法避免这些问题:http://article. gmane.org/gmane.comp.lang.scala.internals/2831
正如 Daniel 指出的,偏向惰性而不是严格可以简化算法并使其更容易编写。
Stream
is toIterator
asimmutable.List
is tomutable.List
. Favouring immutability prevents a class of bugs, occasionally at the cost of performance.scalac itself isn't immune to these problems: 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.