フィルタの代わりにwithFilter


81

後でマップ、フラットマップなどの関数を適用するときに、フィルターの代わりにwithFilterを使用する方が常にパフォーマンスが高くなりますか?

map、flatmap、foreachのみがサポートされているのはなぜですか?(forall / existsのような期待される機能も)


Scalaドキュメントのこの部分にも詳細な説明があります。
ohkts 1119年

回答:


121

Scalaのドキュメントから:

注:の差c filter pとは、c withFilter p後者のみが、後続のドメイン制限前者は、新しいコレクションを作成することでmapflatMapforeach、およびwithFilter動作を制御します。

だから、filter元のコレクションを取得し、新しいコレクションを作成しますが、withFilter非厳密(すなわちなまけ)、後に通過フィルタリングされていない値を渡しますmap/ flatMap/withFilter(フィルタリング)コレクションを通る第2のパスを保存し、通話。したがって、これらの後続のメソッド呼び出しに渡すときに、より効率的になります。

実際、withFilterこれらのメソッドのチェーンを操作するために特別に設計されています。これは、理解のために脱糖化されたものです。これには他のメソッド(forall/などexists)は必要ないため、のFilterMonadic戻り値の型に追加されていませんwithFilter


彼らがいつかこれらのメソッドを追加することを願っています。
キギョ2013年

1
@KigyoあなたがwithFilterを自分で使用することになっているとは思いません(for-expressions内で暗黙的に使用することを除いて)。viewマップ/フィルターを怠惰にしたい場合に使用します。
Luigi Plinge 2013年

そうですか。いただきまし間の正確な違いviewwithFilter?ビューが使用されないのはなぜfor-loopsですか?
キギョ2013年

5
参考までに、コレクション-ヒントとコツは優れた情報を提供する思います。H5は固定されていませんがDon’t create temporary collections、リンクされたセクションで検索できます。
sthzg 2016年

4
の明示的な使用に関してはwithFilter、MartinOdersky自身がCourseraのScalaコースで明示的に使用しています。これを強くお勧めします。彼がそうすることを考えると、違いは通常1文字だけですが、他の人にもそうすることで快適さを与えるかもしれません。たとえば、seq.view filter pseq withFilter p
チャックダニエルズ

9

他にシャドウランズの優れた答え、私は違いの直感的な例持参したい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の考え


1

forall / existsが実装されていない主な理由は、ユースケースが次のとおりであるためです。

  • withFilterを無限のストリーム/反復可能ファイルに遅延適用できます
  • あなたは怠惰に別のwithFilterを適用することができます(そして何度も何度も)

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オブジェクトを元のタイプ(コレクション、ストリーム、イテレーターなど)に変換するためにここで使用されることに注意してください。


1

forall / presents部分の場合:

someList.filter(conditionA).forall(conditionB)

と同じになります(少し直感的ではありませんが)

!someList.exists(conditionA && !conditionB)

同様に、.filter()。exists()を1つのexists()チェックに組み合わせることができますか?



-5

回避策として、あなただけで他の機能を実装することができますmapし、flatMap

さらに、この最適化は小さなコレクションでは役に立ちません…

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.