Scala Collections フレームワークでは、 を使用するときに直感に反する動作がいくつかあると思いますmap()
。
(不変の) コレクションに対する 2 種類の変換を区別できます。結果のコレクションを再作成するために実装が呼び出されるものと、ビルダーを取得するためにnewBuilder
暗黙的に実行されるもの。CanBuildFrom
最初のカテゴリには、含まれる要素の型が変わらないすべての変換が含まれます。たとえば、、、、、、などです。これらの変換は、自由に呼び出してfilter
、呼び出さpartition
れたものと同じコレクション型を再作成することができます。a (またはコレクション フレームワークのアーキテクチャに関するこの記事で説明されている構造の例)をフィルター処理すると、常に別の(または)を返すことができます。それらをフィルタリング変換と呼びましょう。drop
take
span
newBuilder
List[Int]
List[Int]
BitSet
RNA
BitSet
RNA
変換の 2 番目のカテゴリはCanBuildFrom
、含まれる要素の型が変更される可能性があるため、s をより柔軟にする必要があり、その結果、コレクション自体の型を再利用できない可能性BitSet
がありますString
。anには sRNA
のみが含まれますBase
。そのような変換の例はmap
、flatMap
、collect
、scanLeft
、などです。それらをマッピング変換++
と呼びましょう。
さて、ここで議論すべき主な問題です。コレクションの静的な型が何であれ、すべてのフィルタリング変換は同じコレクションの型を返しますが、マッピング操作によって返されるコレクションの型は静的な型によって異なる場合があります。
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)
メソッドの実装に応じて、同じコレクション型を再作成できる場合とできない場合があります。
唯一のポイントは、同じ要素タイプ(コンパイラーが静的に決定できる) を持つコレクションを生成するマッピング操作を使用していることがわかっている場合、フィルター変換の動作方法を模倣して、コレクションのネイティブ ビルダーを使用できるということです。BitSet
sにマッピングするときに再利用したり、同じ順序で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
返されるビルダーのタイプを決定するために使用できます。