scala – 我可以使用TraversableLike.map的类似物来“pimp my li
假设我想将一些功能(如map)添加到Scala列表中,这是列表mapmap f的行,它将函数f应用于列表的每个元素两次. (一个更严重的例子可能是实现并行或分布式地图,但我不想被那个方向的细节分散注意力.)
我的第一个方法是 object MapMap { implicit def createFancyList[A](list: List[A]) = new Object { def mapmap(f: A => A): List[A] = { list map { a: A => f(f(a)) } } } } 现在效果很好 scala> import MapMap._ import MapMap._ scala> List(1,2,3) mapmap { _ + 1 } res1: List[Int] = List(3,4,5) 当然除了列表之外,没有理由我们不希望这个用于任何可穿越的东西,具有地图功能,例如集或流.所以第二次尝试看起来像 object MapMap2 { implicit def createFancyTraversable[A](t: Traversable[A]) = new Object { def mapmap(f: A => A): Traversable[A] = { t map { a: A => f(f(a)) } } } } 但是现在,当然,结果无法分配给List [A]: scala> import MapMap2._ import MapMap2._ scala> val r: List[Int] = List(1,3) mapmap { _ + 1 } <console>:9: error: type mismatch; found : Traversable[Int] required: List[Int] 有一些中间立场吗?我可以编写一个隐式转换,将方法添加到Traversable的所有子类,并成功返回具有该类型的对象吗? (我猜这可能涉及了解可怕的CanBuildFrom特性,甚至可能突破!) 解决方法
您不能对所有Traversable执行此操作,因为它们不保证map返回比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,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 更新2 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,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],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] = 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,That]): That = { val b = bf(repr) b.sizeHint(this) for (x <- this) b += f(x) b.result } Builder b使用原始集合进行初始化,原始集合是通过映射保留动态类型的机制.然而,我们的CanBuildFrom拒绝了所有关于From的知识,通过类型参数Nothing.你可以用Nothing做的就是忽略它,这正是breakOut所做的: def breakOut[From,T,To](implicit b : CanBuildFrom[Nothing,To]) = new CanBuildFrom[From,To] { def apply(from: From) = b.apply(); def apply() = b.apply() } 我们不能调用b.apply(from),只能调用def foo(a:Nothing)= 0. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |