加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 我可以使用TraversableLike.map的类似物来“pimp my li

发布时间:2020-12-16 09:27:56 所属栏目:安全 来源:网络整理
导读:假设我想将一些功能(如map)添加到Scala列表中,这是列表mapmap f的行,它将函数f应用于列表的每个元素两次. (一个更严重的例子可能是实现并行或分布式地图,但我不想被那个方向的细节分散注意力.) 我的第一个方法是 object MapMap { implicit def createFancyLi
假设我想将一些功能(如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
我添加了另一个pimped方法mapToString来演示为什么TraversableW接受两个类型参数,而不是Alexey解决方案中的一个参数.参数CC是较高的kinded类型,它表示原始集合的容器类型.第二个参数A表示原始集合的元素类型.因此,mapToString方法能够返回具有不同元素类型的原始容器类型:CC [String.

更新2
感谢@oxbow_lakes的评论,我已经重新考虑了这一点.确实可以直接pimp CC [X]&lt ;: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,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.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读