たとえば、古いお気に入りの機能があるとします
_def factorial(n:Int) = (BigInt(1) /: (1 to n)) (_*_)
_
ここで、factorial(n)
がLongに適合するn
の最大値を見つけたいと思います。私はそれをできた
_(1 to 100) takeWhile (factorial(_) <= Long.MaxValue) last
_
これは機能しますが、100は任意の大きな数値です。左側に本当に欲しいのは、takeWhile
条件が満たされるまで、より大きな数値を生成し続ける無限ストリームです。
思いついた
_val s = Stream.continually(1).zipWithIndex.map(p => p._1 + p._2)
_
しかし、より良い方法はありますか?
(再帰的に解決策を得ることができることも知っていますが、それは私が探しているものではありません。)
Stream.from(1)
1から始まり1ずつ増加するストリームを作成します。すべてが APIドキュメント にあります。
Iterator
の代わりにStream
を使用することもできます。 Stream
は、計算されたすべての値の参照を保持します。したがって、各値に一度だけアクセスする場合は、反復子の方が効率的です。ただし、イテレータの欠点はその可変性です。
コンパニオンオブジェクト で定義されたIterator
sを作成するための便利なメソッドがいくつかあります。
残念ながら、私が知っているような短い(ライブラリがサポートされている)方法はありません。
_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ライブラリを使用してlast
にIterator
メソッドを実装しても問題ありません。問題は、無限イテレータの最後の要素を取得することが問題になる可能性があることです。したがって、lastWith
を統合するtakeWhile
などのメソッドとして実装する必要があります。
い回避策はsliding
に実装されているIterator
を使用して実行できます。
_scala> Iterator.from(1).sliding(2).dropWhile(_.tail.head < 10).next.head
res12: Int = 9
_
@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
ソリューションを使用...
思い浮かぶもう1つの簡単な例は、フィボナッチ数です。
scala> val it = Iterator.iterate((1,1)){case (a,b) => (b,a+b)}.map(_._1)
it: Iterator[Int] = non-empty iterator
これらの場合、毎回新しい要素をゼロから計算するのではなく、すべての新しい要素に対してO(1)作業を実行します。これにより、実行時間がさらに改善されます。
階乗は毎回ゼロから計算されるため、元の「階乗」関数は最適ではありません。メモ化を使用した最も単純/不変の実装は次のとおりです。
val f : Stream[BigInt] = 1 #:: (Stream.from(1) Zip f).map { case (x,y) => x * y }
そして今、答えは次のように計算できます:
println( "count: " + (f takeWhile (_<Long.MaxValue)).length )
次のバリアントは、現在の値をテストしませんが、最後の有効な番号を見つけて返すためにnext整数をテストします。
Iterator.from(1).find(i => factorial(i+1) > Long.MaxValue).get
ここで.get
を使用しても問題ありません。無限シーケンスのfind
はNone
を返さないからです。