Scalaには、イテレータに非常によく似たStreamクラスがあります。トピック ScalaでのイテレータとStreamの違いは? は、の類似点と相違点に関する洞察を提供します。二つ。
ストリームの使用方法を確認するのは非常に簡単ですが、他のアーティファクトの代わりにストリームを使用するcommonユースケースはあまりありません。
私が今持っているアイデア:
だから私は何か大きな用途を見逃しましたか?それとも、ほとんどの場合、開発者の好みですか?
ありがとう
Stream
とIterator
の主な違いは、後者は変更可能で、いわば「ワンショット」であるのに対し、前者はそうではないということです。 Iterator
はStream
よりもメモリフットプリントが優れていますが、is可変であるという事実は不便な場合があります。
この古典的な素数ジェネレータを例にとってみましょう。
def primeStream(s: Stream[Int]): Stream[Int] =
Stream.cons(s.head, primeStream(s.tail filter { _ % s.head != 0 }))
val primes = primeStream(Stream.from(2))
Iterator
でも簡単に書くことができますが、Iterator
はこれまでに計算された素数をkeepしません。
したがって、Stream
の重要な側面の1つは、最初に複製したり、何度も生成したりせずに、他の関数に渡すことができることです。
高価な計算/無限リストに関しては、これらのことはIterator
でも実行できます。無限リストは実際には非常に便利です-持っていなかったので知らないので、強制された有限サイズを処理するためだけに必要なものよりも複雑なアルゴリズムを見てきました。
ダニエルの答えに加えて、Stream
は評価の短絡に役立つことを覚えておいてください。たとえば、String
を受け取ってOption[String]
を返す関数の膨大なセットがあり、そのうちの1つが機能するまで実行し続けたいとします。
val stringOps = List(
(s:String) => if (s.length>10) Some(s.length.toString) else None ,
(s:String) => if (s.length==0) Some("empty") else None ,
(s:String) => if (s.indexOf(" ")>=0) Some(s.trim) else None
);
まあ、私は確かに全体リストを実行したくありません、そしてList
には「これらを関数として扱う」という便利なメソッドはありませんそして、それらのいずれかがNone
以外の何かを返すまでそれらを実行します。何をすべきか?おそらくこれは:
def transform(input: String, ops: List[String=>Option[String]]) = {
ops.toStream.map( _(input) ).find(_ isDefined).getOrElse(None)
}
これはリストを受け取り、それをStream
(実際には何も評価しない)として扱い、関数を適用した結果である新しいStream
を定義し(ただし、まだ何も評価しません)、最初のリストを検索します定義されている1つ-そしてここでは、魔法のように振り返って、マップを適用し、元のリストから適切なデータを取得する必要があることを認識してから、それをOption[Option[String]]
からOption[String]
に展開しますgetOrElse
を使用します。
次に例を示します。
scala> transform("This is a really long string",stringOps)
res0: Option[String] = Some(28)
scala> transform("",stringOps)
res1: Option[String] = Some(empty)
scala> transform(" hi ",stringOps)
res2: Option[String] = Some(hi)
scala> transform("no-match",stringOps)
res3: Option[String] = None
しかし、それはうまくいきますか?関数にprintln
を挿入して、それらが呼び出されたかどうかを判断できる場合、
val stringOps = List(
(s:String) => {println("1"); if (s.length>10) Some(s.length.toString) else None },
(s:String) => {println("2"); if (s.length==0) Some("empty") else None },
(s:String) => {println("3"); if (s.indexOf(" ")>=0) Some(s.trim) else None }
);
// (transform is the same)
scala> transform("This is a really long string",stringOps)
1
res0: Option[String] = Some(28)
scala> transform("no-match",stringOps)
1
2
3
res1: Option[String] = None
(これはScala 2.8です。2.7の実装では、残念ながら1つオーバーシュートすることがあります。また、do失敗が発生するにつれてNone
の長いリストを蓄積しますが、おそらくこれはここでの実際の計算と比較して安価です。)
リアルタイムでデバイスをポーリングする場合、ストリームの方が便利だと想像できます。
GPSトラッカーを考えてみてください。GPSトラッカーは、要求すると実際の位置を返します。 5分以内にいる場所を事前に計算することはできません。 OpenStreetMapでパスを実現するために数分間だけ使用することも、砂漠や熱帯雨林で6か月以上の遠征に使用することもできます。
または、ハードウェアが稼働していてオンになっている限り、新しいデータを繰り返し返すデジタル温度計やその他の種類のセンサー-ログファイルフィルターは別の例です。
Stream
はIterator
にimmutable.List
はmutable.List
。不変性を優先することで、パフォーマンスを犠牲にして、ある種のバグを防ぐことができます。
scalac自体はこれらの問題の影響を受けません: http://article.gmane.org/gmane.comp.lang.scala.internals/2831
ダニエルが指摘するように、厳密さよりも遅延を優先すると、アルゴリズムが単純化され、アルゴリズムの作成が容易になります。