マップがTraversableよりも具体的なものを返すことを保証しないため、すべてのTraversableに対してこれを行うことはできません。 以下のアップデート2を参照してください。
import collection.generic.CanBuildFrom
import collection.TraversableLike
class TraversableW[CC[X] <: TraversableLike[X, CC[X]], A](value: CC[A]) {
def mapmap(f: A => A)(implicit cbf: CanBuildFrom[CC[A], A, CC[A]]): CC[A]
= value.map(f andThen f)
def mapToString(implicit cbf: CanBuildFrom[CC[A], String, CC[String]]): CC[String]
= value.map(_.toString)
}
object TraversableW {
implicit def TraversableWTo[CC[X] <: TraversableLike[X, CC[X]], A](t: CC[A]): TraversableW[CC, A]
= new TraversableW[CC, A](t)
}
locally {
import TraversableW._
List(1).mapmap(1+)
List(1).mapToString
// The static type of Seq is preserved, *and* the dynamic type of List is also
// preserved.
assert((List(1): Seq[Int]).mapmap(1+) == List(3))
}
更新Alexeyのソリューションのように1つのパラメーターではなく、2つのタイプのパラメーターを受け入れるmapToString
理由を示すため
に、別のpimpedメソッドを追加しました。TraversableW
パラメータCC
はより高い種類のタイプであり、元のコレクションのコンテナタイプを表します。2番目のパラメーター、A
は、元のコレクションの要素タイプを表します。したがって、このメソッドmapToString
は、異なる要素タイプを持つ元のコンテナタイプを返すことができますCC[String
。
UPDATE 2
@oxbow_lakesのコメントのおかげで、私はこれを再考しました。直接ポンピングすることは確かに可能ですがCC[X] <: Traversable[X]
、TraversableLike
厳密には必要ありません。インラインコメント:
import collection.generic.CanBuildFrom
import collection.TraversableLike
class TraversableW[CC[X] <: Traversable[X], A](value: CC[A]) {
/**
* A CanBuildFromInstance based purely the target element type `Elem`
* and the target container type `CC`. This can be converted to a
* `CanBuildFrom[Source, Elem, CC[Elem]` for any type `Source` by
* `collection.breakOut`.
*/
type CanBuildTo[Elem, CC[X]] = CanBuildFrom[Nothing, Elem, CC[Elem]]
/**
* `value` is _only_ known to be a `Traversable[A]`. This in turn
* turn extends `TraversableLike[A, Traversable[A]]`. The signature
* of `TraversableLike#map` requires an implicit `CanBuildFrom[Traversable[A], B, That]`,
* specifically in the call below `CanBuildFrom[Traversable[A], A CC[A]`.
*
* Essentially, the specific type of the source collection is not known in the signature
* of `map`.
*
* This cannot be directly found instead we look up a `CanBuildTo[A, CC[A]]` and
* convert it with `collection.breakOut`
*
* In the first example that referenced `TraversableLike[A, CC[A]]`, `map` required a
* `CanBuildFrom[CC[A], A, CC[A]]` which could be found.
*/
def mapmap(f: A => A)(implicit cbf: CanBuildTo[A, CC]): CC[A]
= value.map[A, CC[A]](f andThen f)(collection.breakOut)
def mapToString(implicit cbf: CanBuildTo[String, CC]): CC[String]
= value.map[String, CC[String]](_.toString)(collection.breakOut)
}
object TraversableW {
implicit def TraversableWTo[CC[X] <: Traversable[X], A](t: CC[A]): TraversableW[CC, A]
= new TraversableW[CC, A](t)
}
locally {
import TraversableW._
assert((List(1)).mapmap(1+) == List(3))
// The static type of `Seq` has been preserved, but the dynamic type of `List` was lost.
// This is a penalty for using `collection.breakOut`.
assert((List(1): Seq[Int]).mapmap(1+) == Seq(3))
}
違いは何ですか?collection.breakOut
単なるから特定のコレクションサブタイプを復元することはできないため、を使用する必要がありましたTraversable[A]
。
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
}
は元のコレクションで初期化されます。Builder
b
これは、を介して動的タイプを保持するメカニズムmap
です。ただし、型引数を使用して、 FromCanBuildFrom
に関するすべての知識を否認しました。あなたができることはそれを無視することだけです、それはまさにそれがすることです:Nothing
Nothing
breakOut
def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) =
new CanBuildFrom[From, T, To] {
def apply(from: From) = b.apply();
def apply() = b.apply()
}
電話をかけることはできません。電話をかけるb.apply(from)
ことができる以上のことはありませんdef foo(a: Nothing) = 0
。