51

Scala には、イテレーターによく似た Stream クラスがあります。トピックScala の Iterator と Stream の違いは? は、両者の類似点と相違点についていくつかの洞察を提供します。

ストリームの使用方法は非常に簡単ですが、他のアーティファクトの代わりにストリームを使用する 一般的なユースケースはあまりありません。

私が今持っているアイデア:

  • 無限シリーズを利用する必要がある場合。しかし、これは私にとって一般的なユースケースとは思えないため、私の基準には合いません。(一般的で盲点がある場合は修正してください)
  • 各要素を計算する必要があるが、何度か再利用したい一連のデータがある場合。これは、開発者人口の大きなサブセットにとって概念的に簡単にフォローできるリストにロードすることができるため、弱いものです。
  • おそらく、大規模なデータ セットまたは計算コストの高い系列があり、必要な項目をすべての要素にアクセスする必要がない可能性が高くなります。ただし、この場合、複数の検索を行う必要がない限り、Iterator が適しています。その場合は、効率が多少悪くても、リストを使用できます。
  • 再利用が必要な一連の複雑なデータがあります。ここでもリストを使用できます。この場合、どちらの場合も同じように使用するのが難しく、すべての要素をロードする必要があるわけではないため、Stream の方が適しています。しかし、それほど一般的ではありません... それともそうですか?

それで、私は大きな用途を逃しましたか?それとも、大部分は開発者の好みですか?

ありがとう

4

4 に答える 4

41

Streamaとanの主な違いはIterator、後者は変更可能で、いわば「ワンショット」であるのに対し、前者はそうではないということです。Iteratorよりもメモリフットプリントが優れていますStreamが、可変であるという事実不便な場合があります。

この古典的な素数ジェネレータを例にとってみましょう。

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はできません。

したがって、aの重要な側面の1つStreamは、最初に複製したり、何度も生成したりすることなく、他の関数に渡すことができることです。

高価な計算/無限リストに関しては、これらのこともIterator同様に行うことができます。無限リストは実際には非常に便利です。リストがなかったためにわからないため、強制された有限サイズを処理するためだけに厳密に必要なアルゴリズムよりも複雑なアルゴリズムを見てきました。

于 2010-01-19T22:27:08.173 に答える
19

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、関数を適用した結果である新しいものを定義し(ただし、まだ何も評価しません)、最初のリストを検索します。が定義されています-そしてここで、魔法のように、マップを適用し、元のリストから適切なデータを取得する必要があることを振り返って認識します-次に、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つオーバーシュートすることがあります。失敗が発生すると、リストが長く蓄積されることに注意してください。ただし、ここでの実際の計算と比較すると、おそらくこれは安価です。)None

于 2010-01-20T04:04:24.280 に答える
7

リアルタイムでデバイスをポーリングする場合、ストリームの方が便利であると想像できます。

尋ねると実際の位置を返す GPS トラッカーを考えてみてください。5 分後にいる場所を事前に計算することはできません。OpenStreetMap でパスを実現するためだけに数分間使用することもあれば、砂漠や熱帯雨林での 6 か月にわたる遠征に使用することもあります。

または、ハードウェアが有効で電源がオンになっている限り、新しいデータを繰り返し返すデジタル温度計やその他の種類のセンサー (ログ ファイル フィルターなど) も別の例です。

于 2011-07-18T12:59:41.473 に答える
3

StreamそのIteratorままにimmutable.Listmutable.List不変性を優先することで、パフォーマンスを犠牲にして、ある種のバグを防ぐことができます。

scalac自体はこれらの問題の影響を受けません:http ://article.gmane.org/gmane.comp.lang.scala.internals/2831

ダニエルが指摘するように、厳密さよりも怠惰を優先すると、アルゴリズムが単純化され、アルゴリズムの作成が容易になります。

于 2010-01-20T08:59:38.017 に答える