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

使用Scala收藏 – CanBuildFrom麻烦

发布时间:2020-12-16 09:39:46 所属栏目:安全 来源:网络整理
导读:我正在尝试编写一个接受任何类型的CC [_]的方法,并将其映射到新的集合(相同的集合类型,但是不同的元素类型),我正在努力争
我正在尝试编写一个接受任何类型的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进行隐式转换。
>我们必须对我们的CanBuildFrom使用breakOut,并对预期的返回值进行类型注释。

这只是因为Repr<:TraversableLike [A,Repr]类型不包括String或Array,因为那些使用隐式转换。 选项3 您可以将所有的隐含结合在一起,并要求用户注释类型。不是最优雅的解决方案,所以我想我会避免发布它,除非你真的很想看到它。 SO,基本上如果你想包括String和Array [T]作为集合,你必须跳过一些环。 Map的类别限制适用于Scala中的String和BitSet函子。 我希望有帮助。如果您还有其他问题,请给我打电话

(编辑:李大同)

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

    推荐文章
      热点阅读