使用Scala收藏 – CanBuildFrom麻烦
我正在尝试编写一个接受任何类型的CC [_]的方法,并将其映射到新的集合(相同的集合类型,但是不同的元素类型),我正在努力争取。基本上我试图实现地图,但不是集合本身。
问题 我试图实现一个具有签名的方法,看起来有点像: def map[CC[_],T,U](cct: CC[T],f: T => U): CC[U] 它的用法是: map(List(1,2,3,4),(_ : Int).toString) //would return List[String] 我对一个答案也感兴趣,而CC是Array,我对我的尝试(下面)最终没有起作用的原因感兴趣。 我的尝试 (由于不耐烦,在下面我完全不能让这个工作,重申,问题是“我该怎么写这样的方法?”) 我这样开始: scala> def map[T,U,CC[_]](cct: CC[T],f: T => U)(implicit cbf: CanBuildFrom[CC[T],CC[U]]): CC[U] = | cct map f ^ <console>:9: error: value map is not a member of type parameter CC[T] cct map f ^ 好的,这是有道理的 – 我需要说CC可以遍历! scala> def map[T,X,CC[X] <: Traversable[X]](cct: CC[T],CC[U]]): CC[U] = | cct map f <console>:10: error: type mismatch; found : Traversable[U] required: CC[U] cct map f ^ 不好,好的!也许如果我实际上指定了cbf实例。毕竟,它将返回类型(To)指定为CC [U]: scala> def map[T,CC[U]]): CC[U] = | cct.map(t => f(t))(cbf) <console>:10: error: type mismatch; found : scala.collection.generic.CanBuildFrom[CC[T],CC[U]] required: scala.collection.generic.CanBuildFrom[Traversable[T],CC[U]] cct.map(t => f(t))(cbf) ^ 不好,好的!这是一个更具体的错误。看起来我可以用这个! scala> def map[T,f: T => U)(implicit cbf: CanBuildFrom[Traversable[T],CC[U]]): CC[U] = | cct.map(t => f(t))(cbf) map: [T,f: T => U)(implicit cbf: scala.collection.generic.CanBuildFrom[Traversable[T],CC[U]])CC[U] 辉煌。我有一张地图!我们来用这个东西! scala> map(List(1,(_ : Int).toString) <console>:11: error: Cannot construct a collection of type List[java.lang.String] with elements of type java.lang.String based on a collection of type Traversable[Int]. map(List(1,(_ : Int).toString) ^ 说什么? 意见 我真的不禁想到托尼·莫里斯当时对此的看法是绝对的。他说什么?他说:“不管怎样,它不是地图”。看看这是多么容易在scalaz风格: scala> trait Functor[F[_]] { def fmap[A,B](fa: F[A])(f: A => B): F[B] } defined trait Functor scala> def map[F[_]: Functor,A,B](fa: F[A],f: A => B): F[B] = implicitly[Functor[F]].fmap(fa)(f) map: [F[_],f: A => B)(implicit evidence$1: Functor[F])F[B] 然后 scala> map(List(1,(_ : Int).toString) <console>:12: error: could not find implicit value for evidence parameter of type Functor[List] map(List(1,(_ : Int).toString) ^ 以便 scala> implicit val ListFunctor = new Functor[List] { def fmap[A,B](fa: List[A])(f: A => B) = fa map f } ListFunctor: java.lang.Object with Functor[List] = $anon$1@4395cbcb scala> map(List(1,(_ : Int).toString) res5: List[java.lang.String] = List(1,4) 备忘自我:听托尼! 解决方法
你所遇到的不一定是CanBuildFrom本身,或者Array vs. Seq问题。你正在运行不高级的String,但是支持与其Chars的映射。
SO:首先是Scala收藏设计的一个偏见。 您需要的是推断集合类型(例如String,Array [Int],List [Foo])和元素类型(例如,与上述对应的Char,Int,Foo)的方法。 Scala 2.10.x添加了几个“类型类”来帮助你。例如,您可以执行以下操作: class FilterMapImpl[A,Repr](val r: GenTraversableLike[A,Repr]) { final def filterMap[B,That](f: A => Option[B])(implicit cbf: CanBuildFrom[Repr,B,That]): That = r.flatMap(f(_).toSeq) } implicit def filterMap[Repr,A](r: Repr)(implicit fr: IsTraversableOnce[Repr]): FilterMapImpl[fr.A,Repr] = new FilterMapImpl(fr.conversion(r)) 这里有两件事。 FIRST,使用集合的类需要两个类型参数:集合Repr的特定类型和元素的类型A. 接下来,您定义一个仅使用集合类型Repr的隐式方法。您使用IsTraversableOnce(注意:还有一个IsTraversableLike)捕获该集合的元素类型。您可以在类型签名FilterMapImpl [Repr,fr.A]中看到这一点。 现在,其中的一部分是因为Scala对于所有“类函数”操作并不使用相同的类别。具体来说,Map是String的有用方法。我可以调整所有字符。但是,String只能是Seq [Char]。如果我想定义一个Functor,那么我的类别只能包含Char类型和Char =>字符。这个逻辑被捕获在CanBuildFrom中。但是,由于String是Seq [Char],如果您尝试在Seq的地图方法支持的类别中使用地图,那么CanBuildFrom会将您的调用更改为映射。 我们基本上定义了我们类别的“继承”关系。如果您尝试使用Functor模式,我们将类型签名放在我们可以保留的最特定类别中。把它称为你要的这是目前收藏设计的一大动力。 结尾解读,回答问题 现在,因为我们试图在同一时间推断出很多类型,我认为这个选项具有最少的类型注释: import collection.generic._ def map[Repr](col: Repr)(implicit tr: IsTraversableLike[Repr]) = new { def apply[U,That](f: tr.A => U)(implicit cbf: CanBuildFrom[Repr,That]) = tr.conversion(col) map f } scala> map("HI") apply (_ + 1 toChar ) warning: there were 2 feature warnings; re-run with -feature for details res5: String = IJ 这里要注意的重要一点是,IsTraversableLike捕获从Repr到TraversableLike的转换,允许您使用map方法。 选项2 我们还将方法调用分开,以便Scala可以在定义我们的匿名函数之前推断类型Repr和U。为了避免对匿名函数进行类型注释,我们必须在出现之前已经知道所有类型。现在,我们仍然可以使用Scala推断出一些类型,但是如果我们这样做,就会丢失隐含的可移植的东西: import collection.generic._ import collection._ def map[Repr <: TraversableLike[A,Repr],That](col: Repr with TraversableLike[A,Repr])(f: A => U)(implicit cbf: CanBuildFrom[Repr,That]) = col map f 请注意,我们必须使用Repr with TraversableLike [A,Repr]。似乎大多数F有界类型都需要这种玩笑。 在任何情况下,现在让我们看看延伸的东西会发生什么: scala> map(List(40,41))(_ + 1 toChar ) warning: there were 1 feature warnings; re-run with -feature for details res8: List[Char] = List(),*) 那很棒。但是,如果我们想要使用与Array和String相同的用法,我们必须多做一些工作: scala> map(Array('H','I'): IndexedSeq[Char])(_ + 1 toChar)(breakOut): Array[Char] warning: there were 1 feature warnings; re-run with -feature for details res14: Array[Char] = Array(I,J) scala> map("HI": Seq[Char])(_ + 1 toChar)(breakOut) : String warning: there were 1 feature warnings; re-run with -feature for details res11: String = IJ 这个用法有两个: >我们必须使用类型注释来从String / Array→Seq / IndexedSeq进行隐式转换。 这只是因为Repr<:TraversableLike [A,Repr]类型不包括String或Array,因为那些使用隐式转换。 选项3 您可以将所有的隐含结合在一起,并要求用户注释类型。不是最优雅的解决方案,所以我想我会避免发布它,除非你真的很想看到它。 SO,基本上如果你想包括String和Array [T]作为集合,你必须跳过一些环。 Map的类别限制适用于Scala中的String和BitSet函子。 我希望有帮助。如果您还有其他问题,请给我打电话 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |