在Scala中有一个Stream类,它非常像迭代器。主题Difference between Iterator and Stream in Scala?提供了对两者之间的相同点和不同点的一些见解。
看到如何使用流非常简单,但我没有很多常见的用例,我会使用流而不是其他工件。
我现在的想法:
所以我错过了任何大用途吗?或者它大部分是开发者偏好?
由于
答案 0 :(得分:40)
Stream
和Iterator
之间的主要区别在于后者是可变的并且是“一次性”,可以这么说,前者不是。 Iterator
的内存占用比Stream
更好,但 可变的事实可能不方便。
拿这个经典素数生成器,例如:
def primeStream(s: Stream[Int]): Stream[Int] =
Stream.cons(s.head, primeStream(s.tail filter { _ % s.head != 0 }))
val primes = primeStream(Stream.from(2))
也可以使用Iterator
轻松编写,但Iterator
因此,Stream
的一个重要方面是,您可以将其传递给其他函数,而不必先将其复制,或者必须反复生成它。
对于昂贵的计算/无限列表,这些事情也可以用Iterator
来完成。无限列表实际上非常有用 - 你只是不知道它,因为你没有它,所以你已经看到了比处理强制有限大小更严格的算法。
答案 1 :(得分:18)
除了Daniel的答案之外,请记住Stream
对于短路评估很有用。例如,假设我有一大堆函数需要String
并返回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
);
好吧,我当然不想执行整个列表,并且List
上没有任何方便的方法说“将这些作为函数对待并执行它们直到其中一个返回None
以外的其他内容。该怎么办?也许这个:
def transform(input: String, ops: List[String=>Option[String]]) = {
ops.toStream.map( _(input) ).find(_ isDefined).getOrElse(None)
}
这需要一个列表并将其视为Stream
(实际上并不评估任何内容),然后定义一个新的Stream
,它是应用函数的结果(但不是然后评估任何一个),然后搜索定义的第一个 - 在这里,神奇地,它回顾并意识到它必须应用地图,并从原始列表中获取正确的数据 - 然后将其从使用Option[Option[String]]
Option[String]
到getOrElse
。{/ p>
以下是一个例子:
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
但它有效吗?如果我们将println
放入我们的函数中,以便我们可以判断它们是否被调用,我们得到
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
(这与Scala 2.8有关;遗憾的是,2.7的实现有时会超过一个。请注意,当你的失败累积时,你做会积累一长串的None
,但是据推测,与你在这里的真实计算相比,这是便宜的。)
答案 2 :(得分:7)
我可以想象,如果您实时轮询某些设备,Stream会更方便。
想象一下GPS追踪器,如果你问它,它会返回实际位置。您无法预先计算5分钟内的位置。您可能只使用它几分钟来实现OpenStreetMap中的路径,或者您可以将它用于沙漠或雨林中六个月的探险。
或者数字温度计或其他类型的传感器,只要硬件处于活动状态并且打开,就会重复返回新数据 - 日志文件过滤器可能是另一个例子。
答案 3 :(得分:3)
Stream
是Iterator
,immutable.List
是mutable.List
。支持不变性可以防止出现一类错误,偶尔会以性能为代价。
scalac本身并不能免疫这些问题:http://article.gmane.org/gmane.comp.lang.scala.internals/2831
正如丹尼尔指出的那样,偏袒懒惰而不是严格可以简化算法并使其更容易编写。