2016-12-04 3 views
2

私はビューが軽量コレクションであり、それが怠惰だと理解します。ビューを軽量化するものを理解したいと思います。スカラコレクションのビュー

私は1000の乱数のリストがあります。私はこのリストの偶数を見つけ、最初の10の偶数だけを選びたいと思う。ここでのビューの使用は、中間のリストespを作成することを避けることができるので、私は最初の10の偶数だけを選択するので、より良いと信じています。当初は、フィルタメソッドで使用する関数は、メソッドforceが呼び出されるまで実行されないため、最適化が達成されたと考えましたが、これは正しくはありません。私はこのシナリオでビューをより良く使うことを理解するために苦労しています。それとも、私は間違った例を選んだのですか?

val r = scala.util.Random 

val l:List[Int] = List.tabulate(1000)(x=>r.nextInt()) 

//without view, I'll get an intermediate list. The function x%2==0 will be on each elemenet of l 
val l1 = l.filter(x=>(x%2 == 0)) 

//this will give size of l2. I got size as 508 but yours could be different depending on the random numbers generated in your case 
l1.size 

//pick 1st 10 even numbers 
val l2 = l1.take(10) 

//using view. I thought that x%2==0 will not be executed right now 

val lv1 = l.view.filter(x=>(x%2 == 0)) 
lv1: scala.collection.SeqView[Int,List[Int]] = SeqViewF(...) 

lv1.size //this is same as l1 size so my assumption that x%2==0 will not be executed is wrong else lv1.size will not be same as l1.size 

val lv2 = lv1.take(10).force 

**質問1 - 私がビューを使用する場合、どのように処理が最適化されますか?

質問2 - lv1はSeqViewF型ですが、Fはフィルタに関連していますが、それはどういう意味ですか?

質問3 - LV1の要素がどのように見えるか何(例えばL1が整数である)**

答えて

0

あなたが書いた:x%2==0が 実行されないだろうというのが私の仮定はlv1.sizel1.size

あなたの仮定と同じにはなりません、他の間違っているよう

lv1.size //これは、L1のサイズと同じです実際には正しいです。違いを測定する手段が間違っているだけです。ご覧のよう

val l:List[Int] = List.fill(10)(util.Random.nextInt) // ten random Ints 

// print every Int that gets tested in the filter 
val lv1 = l.view.filter{x => println(x); x%2 == 0} // no lines printed 

lv1.size // ten Ints sent to STDOUT 

だから、あなたのviewsizeを取ることもその完了を強制します。

0

はええ、それは非常にフィッティングの例ではありません。あなたがやっていることは、イテレーターでもっとうまくいくでしょう:list.filter(_ % 2 == 0).take(10)。これは中間のコレクションを作成せず、最初の10個の要素を超えてリストをスキャンしません(ビューはどちらもそうではありませんが、このケースではちょっとしたオーバーコンプリートです)。

ビューは、遅延操作のシーケンスです。コレクションへの参照と、強制されたときに適用される一連の操作があります。適用される操作が記録される方法はかなり複雑であり、重要ではありません。あなたは正しいと推測しました - SeqViewFは、フィルタが適用されたシーケンスのビューを意味します。 mapの場合は、SeqViewFMなどとなります。

これはいつ必要ですか?

ある例は、あなたが別の場所を渡しているシーケンスを「マッサージ」する必要がある場合です。

def combine(s: Seq[Int]) = s.iterator.zipWithIndex.map { 
    case(x, i) if i % 2 == 0 => x 
    case(x, _) => -x 
}.sum 

、と仮定し、他の人を滴下しながら、あなたは数字の巨大な流れを持っており、あなただけでもものを組み合わせたい:あなたが何らかの形で渡すシーケンスの要素を組み合わせた機能を、持っている、と仮定。あなたはそのために、既存の機能を使用することができます。

valの結果=(stream.view.filter(_%2 == 0))を組み合わせ

をもちろん、combineパラメータはそもそもイテレータとして宣言された場合は、 viewは必要ありませんが、これは必ずしも可能ではありません。時には単にシーケンスを必要とする標準インターフェースを使用する必要がある場合もあります。ここで

も要素がアクセスで計算されているという事実を利用する手の込んだ例です:だから

def notifyUsers(users: Seq[User]) = users 
    .iterator 
    .filter(_.needsNotification) 
    .foreach(_.notify) 

timer.schedule(60 seconds) { notifyUsers(userIDs.view.map(getUser)) } 

、私はいくつかの外部の通知が必要になる場合があり、ユーザーのいくつかのIDを持っていますイベント。私はそれらをuserIDsに保存しました。 タスクが実行されるたびに、通知が必要なすべてのユーザーが検索され、各ユーザーに通知が送信されます。

ここでは、トリックです:notifyUsersは、Userのコレクションをパラメータとしてとります。しかし、私たちが本当に渡しているのは、ユーザーIDの初期セットと、.mapの操作で構成され、それぞれにUserオブジェクトを取得するビューです。その結果、タスクが実行されるたびに新しいUserオブジェクトが各id(おそらくデータベースから)ごとに取得されるため、_needsNotificationフラグが変更されると新しい値が取得されます。

確かに、私はnotifyUsersをidのリストを受け取るように変更することができましたが、代わりにgetUserを実行しますが、それはきれいではありません。まず、この方法で単体テストが簡単になります。getUserを模倣することなく、テストオブジェクトのリストを直接渡すことができます。第二に、このような一般的なユーティリティがより便利です。例えば、Userは、多くの異なるドメインオブジェクトを表す可能性のある特性になる可能性があります。

+0

ありがとうございます。あなたの答えは良いですし、私は実生活のいくつかの例でビューを使用する方法を理解する助けになりました。私は、ビューを使用するときに関数が即座に実行されないという基本的な疑念を明確にしているので、他のコメントを「回答」としてマークしています。決して答えは、決して役に立たなかった –

関連する問題