後でマップ、フラットマップなどの関数を適用するときに、フィルターの代わりにwithFilterを使用する方が常にパフォーマンスが高くなりますか?
map、flatmap、foreachのみがサポートされているのはなぜですか?(forall / existsのような期待される機能も)
回答:
Scalaのドキュメントから:
注:の差
c filter p
とは、c withFilter p
後者のみが、後続のドメイン制限前者は、新しいコレクションを作成することでmap
、flatMap
、foreach
、およびwithFilter
動作を制御します。
だから、filter
元のコレクションを取得し、新しいコレクションを作成しますが、withFilter
非厳密(すなわちなまけ)、後に通過フィルタリングされていない値を渡しますmap
/ flatMap
/withFilter
(フィルタリング)コレクションを通る第2のパスを保存し、通話。したがって、これらの後続のメソッド呼び出しに渡すときに、より効率的になります。
実際、withFilter
これらのメソッドのチェーンを操作するために特別に設計されています。これは、理解のために脱糖化されたものです。これには他のメソッド(forall
/などexists
)は必要ないため、のFilterMonadic
戻り値の型に追加されていませんwithFilter
。
view
マップ/フィルターを怠惰にしたい場合に使用します。
view
とwithFilter
?ビューが使用されないのはなぜfor-loops
ですか?
Don’t create temporary collections
、リンクされたセクションで検索できます。
withFilter
、MartinOdersky自身がCourseraのScalaコースで明示的に使用しています。これを強くお勧めします。彼がそうすることを考えると、違いは通常1文字だけですが、他の人にもそうすることで快適さを与えるかもしれません。たとえば、seq.view filter p
対seq withFilter p
。
他にシャドウランズの優れた答え、私は違いの直感的な例持参したいfilter
としwithFilter
。
次のコードを考えてみましょう
val list = List(1, 2, 3)
var go = true
val result = for(i <- list; if(go)) yield {
go = false
i
}
ほとんどの人はresult
に等しいことを期待していますList(1)
。これは、Scala 2.8以降のケースです。これは、理解のために次のように翻訳されているためです。
val result = list withFilter {
case i => go
} map {
case i => {
go = false
i
}
}
ご覧のとおり、変換によって条件がへの呼び出しに変換されwithFilter
ます。以前のScala2.8では、理解のために次のようなものに翻訳されていました。
val r2 = list filter {
case i => go
} map {
case i => {
go = false
i
}
}
を使用するとfilter
、の値はresult
かなり異なりますList(1, 2, 3)
。フィルタはすでに作成されているため、go
フラグfalse
を作成しているという事実はフィルタに影響を与えません。繰り返しますが、Scala 2.8では、この問題はを使用して解決されwithFilter
ます。withFilter
を使用すると、map
メソッド内で要素にアクセスするたびに条件が評価されます。
参照:-p.120、Scala in action(Scala 2.10をカバー)、Manning Publications、MilanjanRaychaudhuri-理解のための翻訳に関するOderskyの考え
forall / existsが実装されていない主な理由は、ユースケースが次のとおりであるためです。
forall / exitsを実装するには、すべての要素を取得して、怠惰さを失う必要があります。
したがって、たとえば:
import scala.collection.AbstractIterator
class RandomIntIterator extends AbstractIterator[Int] {
val rand = new java.util.Random
def next: Int = rand.nextInt()
def hasNext: Boolean = true
}
//rand_integers is an infinite random integers iterator
val rand_integers = new RandomIntIterator
val rand_naturals =
rand_integers.withFilter(_ > 0)
val rand_even_naturals =
rand_naturals.withFilter(_ % 2 == 0)
println(rand_even_naturals.map(identity).take(10).toList)
//calling a second time we get
//another ten-tuple of random even naturals
println(rand_even_naturals.map(identity).take(10).toList)
ten_rand_even_naturalsはまだイテレータであることに注意してください。toListを呼び出す場合にのみ、乱数が生成され、チェーンでフィルタリングされます
map(identity)はmap(i => i)と同等であり、withFilterオブジェクトを元のタイプ(コレクション、ストリーム、イテレーターなど)に変換するためにここで使用されることに注意してください。
イールドに使用すると、次のような回避策があります。
for {
e <- col;
if e isNotEmpty
} yield e.get(0)