说我有一个功能,例如旧的
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)
但有更好的方法吗?
(我也知道我可以递归地得到一个解决方案,但这不是我想要的。)
答案 0 :(得分:118)
Stream.from(1)
创建一个从1开始并以1递增的流。它全部在API docs中。
答案 1 :(得分:28)
您还可以使用Iterator
代替Stream
。 Stream
保留所有计算值的引用。因此,如果您计划仅访问每个值一次,则迭代器是一种更有效的方法。但迭代器的缺点是它的可变性。
有一些不错的便捷方法可以创建companion object上定义的Iterator
。
不幸的是,我知道没有简短的(支持库)方式来实现像
这样的东西Stream.from(1) takeWhile (factorial(_) <= Long.MaxValue) last
我为Iterator
或drop(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
。