简体   繁体   English

Scala中的无限流

[英]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文档中

A Solution Using Iterators 使用迭代器的解决方案

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

Edit 编辑

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM