使用scala集合 - CanBuildFrom麻烦 [英] Working with scala collections - CanBuildFrom trouble

查看:1181
本文介绍了使用scala集合 - CanBuildFrom麻烦的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想写一个方法,接受任何类型的集合 CC [_] 并将其映射到一个新的集合(相同的集合类型,但不同的元素类型)和我正在奋斗皇家。基本上我试图实现 map 不是在集合本身



问题



我试图实现一个签名的方法,看起来有点像:

  def map [CC [_],T,U](cct:CC [T],f:T => U):CC [U] 

它的用法是:

  map (List(1,2,3,4),(_:Int).toString)//将返回List [String] 

我感兴趣的答案,也可以在 CC Array 对我的尝试(下面)最终失败的原因感兴趣。






我的尝试



(对于不耐烦的,在下面的内容中,我完全没有得到这个工作。重申,问题方法?)



我这样开始:

  scala> def [C,C] [C]:CC [T],U,CC [U] U] = 
| cct map f
^
< console>:9:错误:值映射不是类型参数CC [T]的成员
cct映射f
^

好,我需要说的是 CC 是可遍历的!

  scala> def int [T,U,X,CC [X] <:Traversable [X]](cct:CC [T],f:T => U)(隐含cbf:CanBuildFrom [CC [T] CC [U]]):CC [U] = 
| cct map f
< console>:10:error:type mismatch;
found:Traversable [U]
必需:CC [U]
cct map f
^

Err,OK!也许如果我实际上指定 cbf 实例。毕竟,它将返回类型( To )指定为 CC [U]

  scala> def int [T,U,X,CC [X] <:Traversable [X]](cct:CC [T],f:T => U)(隐含cbf:CanBuildFrom [CC [T] CC [U]]):CC [U] = 
| cct.map(t => f(t))(cbf)
< console>:10:error:type mismatch;
found:scala.collection.generic.CanBuildFrom [CC [T],U,CC [U]]
必需:scala.collection.generic.CanBuildFrom [Traversable [T],U,CC [U ]]
cct.map(t => f(t))(cbf)
^


b $ b

Err,OK!这是一个更具体的错误。看起来我可以使用它!

  scala> def implicit [T,U,X,CC [X] <:Traversable [X]](cct:CC [T],f:T => U)(隐式cbf:CanBuildFrom [Traversable [T],U, CC [U]]):CC [U] = 
| cct.map(t => f(t))(cbf)
map:[T,U,X,CC [X] <:Traversable [X]](cct:CC [T] :T => U)(隐含cbf:scala.collection.generic.CanBuildFrom [Traversable [T],U,CC [U]])CC [U]

辉煌。我有一个地图!让我们使用这个东西!

  scala> map(List(1,2,3,4),(_:Int).toString)
< console>:11:错误:无法构造类型为List [java.lang.String]的类型java.lang.String基于类型Traversable [Int]的集合。
map(List(1,2,3,4),(_:Int).toString)
^

说,什么?






观察结果



我真的不能不这样认为,托尼·莫里斯对当时的看法是绝对的。他说什么?他说无论是什么,它不是地图。看看这是多么容易这是scalaz风格

  scala> trait Functor [F [_]] {def fmap [A​​,B](fa:F [A])(f:A => B):F [B]} 
define trait Functor

scala> def map [F [_]:Functor,A,B](fa:F [A],f:A => B) )
map:[F [_],A,B](fa:F [A],f:A => B)(隐含证据$ 1:Functor [F])F [B]

  scala> map(List(1,2,3,4),(_:Int).toString)
< console>:12:error:找不到类型Functor的evidence参数的隐式值
map(List(1,2,3,4),(_:Int).toString)
^

因此

  scala>隐含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,2,3,4),(_:Int).toString)
res5:List [java.lang.String] = List(1,2,3,4)



自我备忘:听Tony!

CanBuildFrom 本身,或者 Array / code>与 Seq 问题。您正在使用 String ,这是不是较高类型,但支持 map Char



SO:首先是Scala的集合设计。



是一种推断集合类型的方法(例如 String Array [Int] 列表[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)(隐含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 => Char 。这个逻辑在 CanBuildFrom 中捕获。但是,由于 String Seq [Char] ,如果您尝试使用 Seq 方法支持的类别中 c> CanBuildFrom 将改变您对 map 的调用。



我们的类别的继承关系。如果您尝试使用 Functor 模式,我们将类型签名放到我们可以保留的最具体类别。称它为你会的;











b

现在,因为我们试图同时推断出很多类型,我认为这个选项有最少的类型注释:

  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,U,That])=
tr.conversion(col)map f
}


scala>地图(HI)适用(_ + 1 toChar)
警告:有2个功能警告;使用-feature重新运行细节
res5:String = IJ

注意这里是 IsTraversableLike 捕获从 Repr TraversableLike

选项2

>

我们还将该方法调用了一点,以便Scala可以推断出 Repr U 之前我们定义我们的匿名函数。要避免匿名函数的类型注释,我们必须先显示所有类型已知。现在,我们仍然可以让Scala推断一些类型,但是如果我们这样做,则会丢失隐含的 Traversable

  import collection.generic._ 
import collection._
def map [Repr< ;: TraversableLike [A,Repr],A ,U,That](col:Repr with TraversableLike [A,Repr])(f:A => U)(implicit cbf:CanBuildFrom [Repr,U,That])=
col map f $ b $注意,我们必须使用 Repr与TraversableLike [A,Repr]

$ c>。



无论如何,现在让我们看看在 Traversable

  scala> map(List(40,41))(_ + 1 toChar)
警告:有1个功能警告;使用-feature查看详细信息
res8:List [Char] = List(),*)


$ b b

太棒了。但是,如果我们想要 Array String 使用相同的用法,我们必须多做一些工作: / p>

  scala> map(Array('H','I'):IndexedSeq [Char])(_ + 1 toChar)(breakOut):Array [Char] 
警告: re-run with -feature for details
res14:Array [Char] = Array(I,J)

scala> map(HI:Seq [Char])(_ + 1 toChar)(breakOut):String
警告:使用-feature重新运行细节
res11:String = IJ

到这个用法:


  1. 我们必须使用类型注释来隐式转换 String / 数组 Seq / IndexedSeq li>
  2. 我们必须使用 breakOut 为我们的 CanBuildFrom

    这是因为 Repr< ;: TraversableLike [A,Repr] 不包括 String Array ,因为那些使用隐式转换。



    选项3



    您可以将所有含义放在一起,并要求用户注释类型。不是最优雅的解决方案,所以我想避免发布,除非你真的想看到它。



    基本上如果你想包括 String Array [T] 作为集合,你必须跳过一些圈。地图的类别限制适用于Scala中的 String BitSet functors。



    我希望有所帮助。如果您还有其他问题,请给我打电话。


    I'm trying to write a method which accepts any type of collection CC[_] and maps it to a new collection (the same collection type but a different element type) and I am struggling royally. Basically I'm trying to implement map but not on the collection itself.

    The Question

    I'm trying to implement a method with a signature which looks a bit like:

    def map[CC[_], T, U](cct: CC[T], f: T => U): CC[U]
    

    It's usage would be:

    map(List(1, 2, 3, 4), (_ : Int).toString) //would return List[String]
    

    I'm interested in an answer which would also work where CC is Array and I'm interested in the reason my attempts (below) have ultimately not worked.


    My Attempts

    (For the impatient, in what follows, I utterly fail to get this to work. To reiterate, the question is "how can I write such a method?")

    I start like this:

    scala> def map[T, U, CC[_]](cct: CC[T], f: T => U)(implicit cbf: CanBuildFrom[CC[T], U, CC[U]]): CC[U] = 
         | cct map f
                                                                 ^
     <console>:9: error: value map is not a member of type parameter CC[T]
           cct map f
               ^
    

    OK, that makes sense - I need to say that CC is traversable!

    scala> def map[T, U, X, CC[X] <: Traversable[X]](cct: CC[T], f: T => U)(implicit cbf: CanBuildFrom[CC[T], U, CC[U]]): CC[U] = 
         | cct map f
    <console>:10: error: type mismatch;
     found   : Traversable[U]
     required: CC[U]
           cct map f
               ^
    

    Err, OK! Maybe if I actually specify that cbf instance. After all, it specifies the return type (To) as CC[U]:

    scala> def map[T, U, X, CC[X] <: Traversable[X]](cct: CC[T], f: T => U)(implicit cbf: CanBuildFrom[CC[T], U, CC[U]]): CC[U] = 
         | cct.map(t => f(t))(cbf)
    <console>:10: error: type mismatch;
     found   : scala.collection.generic.CanBuildFrom[CC[T],U,CC[U]]
     required: scala.collection.generic.CanBuildFrom[Traversable[T],U,CC[U]]
           cct.map(t => f(t))(cbf)
                              ^
    

    Err, OK! That's a more specific error. Looks like I can use that!

    scala> def map[T, U, X, CC[X] <: Traversable[X]](cct: CC[T], f: T => U)(implicit cbf: CanBuildFrom[Traversable[T], U, CC[U]]): CC[U] = 
         | cct.map(t => f(t))(cbf)
    map: [T, U, X, CC[X] <: Traversable[X]](cct: CC[T], f: T => U)(implicit cbf: scala.collection.generic.CanBuildFrom[Traversable[T],U,CC[U]])CC[U]
    

    Brilliant. I has me a map! Let's use this thing!

    scala> map(List(1, 2, 3, 4), (_ : 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, 2, 3, 4), (_ : Int).toString)
                     ^
    

    Say, what?


    Observations

    I really can't help but think that Tony Morris' observations about this at the time were absolutely spot on. What did he say? He said "Whatever that is, it is not map". Look at how easy this is in scalaz-style:

    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[_], A, B](fa: F[A], f: A => B)(implicit evidence$1: Functor[F])F[B]
    

    Then

    scala> map(List(1, 2, 3, 4), (_ : Int).toString)
    <console>:12: error: could not find implicit value for evidence parameter of type Functor[List]
                  map(List(1, 2, 3, 4), (_ : Int).toString)
                     ^
    

    So that

    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, 2, 3, 4), (_ : Int).toString)
    res5: List[java.lang.String] = List(1, 2, 3, 4)
    

    Memo to self: listen to Tony!

    解决方案

    What you're running into is not necessarily CanBuildFrom itself, or the Array vs. Seq issue. You're running into String which is not higher-kinded, but supports map against its Chars.

    SO: First a digression into Scala's collection design.

    What you need is a way to infer both the collection type (e.g. String, Array[Int], List[Foo]) and the element type (e.g. Char, Int, Foo corresponding to the above).

    Scala 2.10.x has added a few "type classes" to help you. For example, you can do the following:

    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))
    

    There's two pieces here. FIRST, your class that uses collections needs two type parameters: The specific type of the collection Repr and the type of the elements A.

    Next, you define an implicit method which only takes the collection type Repr. You use the IsTraversableOnce (note: there is also an IsTraversableLike) to capture the element type of that collection. You see this used in the type signature FilterMapImpl[Repr, fr.A].

    Now, part of this is because Scala does not use the same category for all of its "functor-like" operations. Specifically, map is a useful method for String. I can adjust all characters. However, String can only be a Seq[Char]. If I want to define a Functor, then my category can only contain the type Char and the arrows Char => Char. This logic is captured in CanBuildFrom. However, since a String is a Seq[Char], if you try to use a map in the category supported by Seq's map method, then CanBuildFrom will alter your call to map.

    We're essentially defining an "inheritance" relationship for our categories. If you try to use the Functor pattern, we drop the type signature to the most specific category we can retain. Call it what you will; that's a big motivating factor for the current collection design.

    End Digression, answer the question

    Now, because we're trying to infer a lot of types at the same time, I think this option has the fewest type annotations:

    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, U, 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
    

    The important piece to note here is that IsTraversableLike captures a conversion from Repr to TraversableLike that allows you to use the map method.

    Option 2

    We also split the method call up a bit so that Scala can infer the types Repr and U before we define our anonymous function. To avoid type annotations on anonymous functions, we must have all types known before it shows up. Now, we can still have Scala infer some types, but lose things that are implicitly Traversable if we do this:

    import collection.generic._
    import collection._
    def map[Repr <: TraversableLike[A, Repr], A, U, That](col: Repr with TraversableLike[A,Repr])(f: A => U)(implicit cbf: CanBuildFrom[Repr, U, That]) = 
        col map f
    

    Notice that we have to use Repr with TraversableLike[A,Repr]. It seems that most F-bounded types require this juggling.

    In any case, now let's see what happens on something that extends Traversable:

    scala> map(List(40,41))(_ + 1 toChar )
    warning: there were 1 feature warnings; re-run with -feature for details
    res8: List[Char] = List(), *)
    

    That's great. However, if we want the same usage for Array and String, we have to go to a bit more work:

    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
    

    There are two pieces to this usage:

    1. We have to use a type annotation for the implicit conversion from String/ArraySeq/IndexedSeq.
    2. We have to use breakOut for our CanBuildFrom and type-annotate the expected return value.

    This is solely because the type Repr <: TraversableLike[A,Repr] does not include String or Array, since those use implicit conversions.

    Option 3

    You can place all the implicits together at the end and require the user to annotate types. Not the most elegant solution, so I think I'll avoid posting it unless you'd really like to see it.

    SO, basically if you want to include String and Array[T] as collections, you have to jump through some hoops. This category restriction for map applies to both String and BitSet functors in Scala.

    I hope that helps. Ping me if you have any more questions.

    这篇关于使用scala集合 - CanBuildFrom麻烦的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆