Scala中的无限流

时间:2011-06-20 07:47:00

标签: scala stream

说我有一个功能,例如旧的

def factorial(n:Int) = (BigInt(1) /: (1 to n)) (_*_)

现在我想找到n适合其中factorial(n)的最大值。我能做到

(1 to 100) takeWhile (factorial(_) <= Long.MaxValue) last

这有效,但100是任意大数;在左侧我真正想要的是一个无限的流,它会一直产生更高的数字,直到满足takeWhile条件。

我想出了

val s = Stream.continually(1).zipWithIndex.map(p => p._1 + p._2)

但有更好的方法吗?

(我也知道我可以递归地得到一个解决方案,但这不是我想要的。)

5 个答案:

答案 0 :(得分:118)

Stream.from(1)

创建一个从1开始并以1递增的流。它全部在API docs中。

答案 1 :(得分:28)

使用迭代器的解决方案

您还可以使用Iterator代替StreamStream保留所有计算值的引用。因此,如果您计划仅访问每个值一次,则迭代器是一种更有效的方法。但迭代器的缺点是它的可变性。

有一些不错的便捷方法可以创建companion object上定义的Iterator

修改

不幸的是,我知道没有简短的(支持库)方式来实现像

这样的东西
Stream.from(1) takeWhile (factorial(_) <= Long.MaxValue) last

我为Iteratordrop(n: Int)推送dropWhile一定数量元素的方法是:

Iterator.from(1).dropWhile( factorial(_) <= Long.MaxValue).next - 1

- 1适用于此特殊用途,但不是一般解决方案。但是使用pimp my library在last上实现Iterator方法应该没问题。问题是无限迭代器的最后一个元素可能会有问题。因此,它应该像lastWith集成takeWhile这样的方法实现。

可以使用sliding实现一个丑陋的解决方法,该Iterator是为scala> Iterator.from(1).sliding(2).dropWhile(_.tail.head < 10).next.head res12: Int = 9 实现的:

{{1}}

答案 2 :(得分:4)

正如@ziggystar指出的那样,Streams将先前计算的值列表保存在内存中,因此使用Iterator是一个很好的改进。

为了进一步改进答案,我认为“无限流”通常是基于预先计算的值计算(或可以计算)。如果是这种情况(并且在你的析因流中肯定是这样),我建议改为使用Iterator.iterate

看起来大致如下:

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

然后,您可以执行以下操作:

scala> it.find(_._2 >= Long.MaxValue).map(_._1).get - 1
res0: Int = 22

或使用@ziggystar sliding解决方案......

想到另一个简单的例子,就是斐波纳契数字:

scala> val it = Iterator.iterate((1,1)){case (a,b) => (b,a+b)}.map(_._1)
it: Iterator[Int] = non-empty iterator

在这些情况下,你不是每次都从头开始计算你的新元素,而是为每个新元素做一个O(1)工作,这样可以进一步提高你的运行时间。

答案 3 :(得分:1)

原作&#34; factorial&#34;函数不是最优的,因为每次都是从零开始计算阶乘。使用memoization的最简单/不可变的实现是这样的:

val f : Stream[BigInt] = 1 #:: (Stream.from(1) zip f).map { case (x,y) => x * y }

现在,答案可以像这样计算:

println( "count: " + (f takeWhile (_<Long.MaxValue)).length )

答案 4 :(得分:0)

以下变体不测试当前值,而是 next 整数,以查找并返回最后一个有效数字:

Iterator.from(1).find(i => factorial(i+1) > Long.MaxValue).get

在这里使用.get是可以接受的,因为在无限序列上的find将永远不会返回None