5

Scala Collections フレームワークでは、 を使用するときに直感に反する動作がいくつかあると思いますmap()

(不変の) コレクションに対する 2 種類の変換を区別できます。結果のコレクションを再作成するために実装が呼び出されるものと、ビルダーを取得するためにnewBuilder暗黙的に実行されるもの。CanBuildFrom

最初のカテゴリには、含まれる要素の型が変わらないすべての変換が含まれます。たとえば、、、、、、などです。これらの変換は、自由に呼び出してfilter、呼び出さpartitionれたものと同じコレクション型を再作成することができます。a (またはコレクション フレームワークのアーキテクチャに関するこの記事で説明されている構造の例)をフィルター処理すると、常に別の(または)を返すことができます。それらをフィルタリング変換と呼びましょう。droptakespannewBuilderList[Int]List[Int]BitSetRNABitSetRNA

変換の 2 番目のカテゴリはCanBuildFrom、含まれる要素の型が変更される可能性があるため、s をより柔軟にする必要があり、その結果、コレクション自体の型を再利用できない可能性BitSetがありますString。anには sRNAのみが含まれますBase。そのような変換の例はmapflatMapcollectscanLeft、などです。それらをマッピング変換++と呼びましょう。

さて、ここで議論すべき主な問題です。コレクションの静的な型が何であれ、すべてのフィルタリング変換は同じコレクションの型を返しますが、マッピング操作によって返されるコレクションの型は静的な型によって異なる場合があります。

scala> import collection.immutable.TreeSet
import collection.immutable.TreeSet

scala> val treeset = TreeSet(1,2,3,4,5) // static type == dynamic type
treeset: scala.collection.immutable.TreeSet[Int] = TreeSet(1, 2, 3, 4, 5)

scala> val set: Set[Int] = TreeSet(1,2,3,4,5) // static type != dynamic type
set: Set[Int] = TreeSet(1, 2, 3, 4, 5)

scala> treeset.filter(_ % 2 == 0)
res0: scala.collection.immutable.TreeSet[Int] = TreeSet(2, 4) // fine, a TreeSet again

scala> set.filter(_ % 2 == 0)    
res1: scala.collection.immutable.Set[Int] = TreeSet(2, 4) // fine

scala> treeset.map(_ + 1)        
res2: scala.collection.immutable.SortedSet[Int] = TreeSet(2, 3, 4, 5, 6) // still fine

scala> set.map(_ + 1)    
res3: scala.collection.immutable.Set[Int] = Set(4, 5, 6, 2, 3) // uh?!

これで、なぜこのように機能するのかがわかりました。所々に説明る。CanBuildFromつまり、静的な型に基づいて暗黙的に挿入され、そのdef apply(from: Coll)メソッドの実装に応じて、同じコレクション型を再作成できる場合とできない場合があります。

唯一のポイントは、同じ要素タイプ(コンパイラーが静的に決定できる) を持つコレクションを生成するマッピング操作を使用していることがわかっている場合、フィルター変換の動作方法を模倣して、コレクションのネイティブ ビルダーを使用できるということです。BitSetsにマッピングするときに再利用したり、同じ順序でInt新しいものを作成したりできます。TreeSet

次に、次のようなケースを回避します。

for (i <- set) {
  val x = i + 1
  println(x)
}

TreeSetのインクリメントされた要素を同じ順序で出力しません

for (i <- set; x = i + 1)
  println(x)

そう:

  • 説明されているように、マッピング変換の動作を変更することは良い考えだと思いますか?
  • 私がひどく見落とした避けられない警告は何ですか?
  • どのように実装できますか?

パラメータのようなものについて考えていましたimplicit sameTypeEvidence: A =:= B。おそらくデフォルト値はnull(またはむしろimplicit canReuseCalleeBuilderEvidence: B <:< A = null) で、実行時に使用して、より多くの情報を に提供し、CanBuildFrom返されるビルダーのタイプを決定するために使用できます。

4

1 に答える 1

1

もう一度見てみましたが、あなたの問題はScalaコレクションの特定の欠陥からではなく、のビルダーの欠落から生じていると思いますTreeSet。以下は意図したとおりに機能するためです。

val list = List(1,2,3,4,5)
val seq1: Seq[Int] = list
seq1.map( _ + 1 ) // yields List

val vector = Vector(1,2,3,4,5)
val seq2: Seq[Int] = vector
seq2.map( _ + 1 ) // yields Vector

その理由はTreeSet、特殊なコンパニオンオブジェクト/ビルダーが欠落しているためです。

seq1.companion.newBuilder[Int]    // ListBuffer
seq2.companion.newBuilder[Int]    // VectorBuilder
treeset.companion.newBuilder[Int] // Set (oops!)

RNAだから私の推測では、あなたがあなたのクラスのためにそのような仲間のために適切な準備をするならば、あなたはそれを見つけてあなたが望むようmapに働くかもしれません...?filter

于 2011-04-17T16:18:47.597 に答える