[英]Infinite streams in Scala
Say I have a function, for example the old favourite 说我有一个功能,例如旧的最爱
def factorial(n:Int) = (BigInt(1) /: (1 to n)) (_*_)
Now I want to find the biggest value of n
for which factorial(n)
fits in a Long. 现在我想找到n
最大值,其中factorial(n)
适合Long。 I could do 我可以
(1 to 100) takeWhile (factorial(_) <= Long.MaxValue) last
This works, but the 100 is an arbitrary large number; 这是有效的,但100是任意大数; what I really want on the left hand side is an infinite stream that keeps generating higher numbers until the takeWhile
condition is met. 我真正想要的是在左侧是一个无限的流,它会一直产生更高的数字,直到满足takeWhile
条件。
I've come up with 我想出来了
val s = Stream.continually(1).zipWithIndex.map(p => p._1 + p._2)
but is there a better way? 但有更好的方法吗?
(I'm also aware I could get a solution recursively but that's not what I'm looking for.) (我也知道我可以递归地得到一个解决方案,但这不是我正在寻找的。)
Stream.from(1)
创建一个从1开始并递增1的流。这些都在API文档中 。
You can also use an Iterator
instead of a Stream
. 您也可以使用Iterator
而不是Stream
。 The Stream
keeps references of all computed values. Stream
保留所有计算值的引用。 So if you plan to visit each value only once, an iterator is a more efficient approach. 因此,如果您计划仅访问每个值一次,则迭代器是一种更有效的方法。 The downside of the iterator is its mutability, though. 但迭代器的缺点是它的可变性。
There are some nice convenience methods for creating Iterator
s defined on its companion object . 有一些很好的便捷方法可以创建在其伴随对象上定义的Iterator
。
Unfortunately there's no short (library supported) way I know of to achieve something like 不幸的是,我没有简短的(支持库)方式来实现类似的东西
Stream.from(1) takeWhile (factorial(_) <= Long.MaxValue) last
The approach I take to advance an Iterator
for a certain number of elements is drop(n: Int)
or dropWhile
: 我用来推进Iterator
一定数量元素的方法是drop(n: Int)
或dropWhile
:
Iterator.from(1).dropWhile( factorial(_) <= Long.MaxValue).next - 1
The - 1
works for this special purpose but is not a general solution. - 1
用于此特殊目的但不是一般解决方案。 But it should be no problem to implement a last
method on an Iterator
using pimp my library. 但是使用pimp my library在Iterator
上实现last
方法应该没问题。 The problem is taking the last element of an infinite Iterator could be problematic. 问题是无限迭代器的最后一个元素可能会有问题。 So it should be implemented as method like lastWith
integrating the takeWhile
. 因此它应该像lastWith
集成takeWhile
一样实现。
An ugly workaround can be done using sliding
, which is implemented for Iterator
: 可以使用sliding
来完成一个丑陋的解决方法,这是为Iterator
实现的:
scala> Iterator.from(1).sliding(2).dropWhile(_.tail.head < 10).next.head
res12: Int = 9
as @ziggystar pointed out, Streams
keeps the list of previously computed values in memory, so using Iterator
is a great improvment. 正如@ziggystar指出的那样, Streams
将先前计算的值列表保存在内存中,因此使用Iterator
是一个很大的改进。
to further improve the answer, I would argue that "infinite streams", are usually computed (or can be computed) based on pre-computed values. 为了进一步改进答案,我认为“无限流”通常是基于预先计算的值来计算(或可以计算)。 if this is the case (and in your factorial stream it definately is), I would suggest using Iterator.iterate
instead. 如果是这种情况(并且在你的析因流中肯定是这样),我建议改用Iterator.iterate
。
would look roughly like this: 看起来大致如下:
scala> val it = Iterator.iterate((1,BigInt(1))){case (i,f) => (i+1,f*(i+1))}
it: Iterator[(Int, scala.math.BigInt)] = non-empty iterator
then, you could do something like: 那么,你可以这样做:
scala> it.find(_._2 >= Long.MaxValue).map(_._1).get - 1
res0: Int = 22
or use @ziggystar sliding
solution... 或使用@ziggystar sliding
解决方案......
another easy example that comes to mind, would be fibonacci numbers: 另一个容易想到的例子是斐波那契数字:
scala> val it = Iterator.iterate((1,1)){case (a,b) => (b,a+b)}.map(_._1)
it: Iterator[Int] = non-empty iterator
in these cases, your'e not computing your new element from scratch every time, but rather do an O(1) work for every new element, which would improve your running time even more. 在这些情况下,你不是每次都从头开始计算你的新元素,而是为每个新元素做一个O(1)工作,这将进一步改善你的运行时间。
The original "factorial" function is not optimal, since factorials are computed from scratch every time. 原始的“阶乘”函数不是最优的,因为每次都是从零开始计算阶乘。 The simplest/immutable implementation using memoization is like this: 使用memoization的最简单/不可变的实现是这样的:
val f : Stream[BigInt] = 1 #:: (Stream.from(1) zip f).map { case (x,y) => x * y }
And now, the answer can be computed like this: 而现在,答案可以像这样计算:
println( "count: " + (f takeWhile (_<Long.MaxValue)).length )
The following variant does not test the current, but the next integer, in order to find and return the last valid number: 以下变量不测试当前,而是下一个整数,以便查找并返回最后一个有效数字:
Iterator.from(1).find(i => factorial(i+1) > Long.MaxValue).get
Using .get
here is acceptable, since find
on an infinite sequence will never return None
. 在这里使用.get
是可以接受的,因为在无限序列上find
永远不会返回None
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.