CanBuildFrom
それが起こる理由の説明は長い話です: Scala 2.9.x (他のバージョンについては知りません) では、filter や map などのコレクション メソッドはメカニズムに依存しています。アイデアは、新しいコレクションのビルダーを作成するために使用される暗黙的なパラメーターがあるということです。
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
val b = bf(repr)
b.sizeHint(this)
for (x <- this) b += f(x)
b.result
}
このメカニズムのおかげで、マップ メソッドはTraversableLike
トレイトでのみ定義され、そのサブクラスはそれをオーバーライドする必要がありません。ご覧のとおり、メソッド マップ シグネチャ内には多くの型パラメーターがあります。些細なことを見てみましょう:
B
新しいコレクションの要素の型である
- は
A
、ソース コレクション内の要素の型です
より複雑なものを見てみましょう:
That
コレクションの新しいタイプで、現在のタイプとは異なる場合があります。古典的な例は、たとえば toString を使用して BitSet をマップする場合です。
scala> val a = BitSet(1,3,5)
a scala.collection.immutable.BitSet = BitSet(1, 3, 5)
scala> a.map {_.toString}
res2: scala.collection.immutable.Set[java.lang.String] = Set(1, 3, 5)
BitSet[String]
マップを作成することは違法であるため、Set[String]
最終的Repr
には現在のコレクションのタイプになります。コレクションを関数にマップしようとすると、コンパイラは型パラメーターを使用して適切な CanBuildFrom を解決します。
当然のことながら、 map メソッドはParIterableLike
次のように並列コレクションでオーバーライドされています。
def map[S, That](f: T => S)(implicit bf: CanBuildFrom[Repr, S, That]): That = bf ifParallel { pbf =>
executeAndWaitResult(new Map[S, That](f, pbf, splitter) mapResult { _.result })
} otherwise seq.map(f)(bf2seq(bf))
ご覧のとおり、メソッドのシグネチャは同じですが、別のアプローチを使用しています。提供され CanBuildFrom
たものが並列かどうかをテストし、そうでない場合はデフォルトの実装にフォールバックします。そのため、Scala 並列コレクションは、map メソッドの並列ビルダーを作成する特別な CanBuildFrom (並列コレクション) を使用します。
ただし、実行するとどうなりますか
(if(runParallel) theList.par else theList) map println //Doesn't run in parallel
の結果に対して実行される map メソッドです。
(if(runParallel) theList.par else theList)
その戻り値の型は、両方のクラスの最初の共通の祖先です (この場合、特定の数の特性が混在しています)。これは共通の祖先であるため、型パラメーターRepr
は両方のコレクション表現のある種の共通の祖先になるので、それを と呼びましょうRepr1
。
結論
メソッドを呼び出すとmap
、コンパイラはCanBuildFrom[Repr, B, That]
操作に適した を見つける必要があります。私たちのは並列コレクションのものではないため、並列ビルダーを提供できるRepr1
ものはありません。CanBuildFrom[Repr1,B,That]
非並列コレクションのすべてのマップも並列に実行されることを意味する動作が異なる場合、これは実際には Scala コレクションの実装に関しては正しい動作です。
ここでのポイントは、Scala コレクションが 2.9.x でどのように設計されているかについて、代替手段がないということです。CanBuildFrom
コンパイラが並列コレクションに を提供しない場合、マップは並列になりません。