Sie das nicht für alle Traversables tun können, da sie don Ich garantiere, dass die Map etwas Spezifischeres als Traversable zurückgibt.
Siehe Update 2 unten.
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))
}
UPDATE Ich habe eine andere gepimpt Methode hinzugefügt, mapToString
, zu zeigen, warum TraversableW
zwei Typparameter akzeptiert, sondern als einen Parameter wie in Alexey Lösung. Der Parameter CC
ist ein höherer Typ, er stellt den Containertyp der ursprünglichen Sammlung dar. Der zweite Parameter A
repräsentiert den Elementtyp der ursprünglichen Sammlung. Die Methode mapToString
kann somit den ursprünglichen Containertyp mit einem anderen Elementtyp zurückgeben: CC[String
.
UPDATE 2 Dank @oxbow_lakes Kommentar habe ich diese überdacht. Es ist in der Tat möglich, direkt zu pimpen CC[X] <: Traversable[X]
, TraversableLike
ist nicht unbedingt erforderlich. Kommentare inline:
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))
}
Was ist der Unterschied?Wir mussten collection.breakOut
verwenden, da wir den spezifischen Sammlungsuntertyp nicht von Traversable[A]
wiederherstellen können.
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
}
Die Builder
b
wird mit der ursprünglichen Sammlung initialisiert, der der Mechanismus ist, den dynamischen Typ durch eine map
zu bewahren. Allerdings desavouiert unsere CanBuildFrom
alles Wissen der Von, durch die Art Argument Nothing
. Alles, was Sie mit Nothing
tun können, ist es zu ignorieren, was genau das ist, was breakOut
tut:
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()
}
Wir b.apply(from)
nicht nennen kann, nicht mehr als Sie def foo(a: Nothing) = 0
nennen könnte.
Funktioniert perfekt! –
Ich nahm einen etwas anderen Ansatz in Scalaz, die ein bisschen stärker ist: http://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/CanBuildAnySelf.scala#L24 http: //github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/Functor.scala#L28 – retronym
Ich habe nicht die Rechte, es zu bearbeiten, retronym, aber vielleicht das formatierte des Codes Block sollte erweitert werden, um die Importe in Ihrem Beispiel aufzunehmen? Prost! – pr1001