代码之家  ›  专栏  ›  技术社区  ›  Scott Morrison

如何组成scala-sortedmap的联合体?

  •  6
  • Scott Morrison  · 技术社区  · 15 年前

    (我使用的是scala夜谎言,在2.8.0b1rc4中看到了同样的行为。我是一个新来的斯卡拉人。)

    我有两个 SortedMap 我想组成的联盟。下面是我要使用的代码:

    import scala.collection._
    
    object ViewBoundExample {
        class X
        def combine[Y](a: SortedMap[X, Y], b: SortedMap[X, Y]): SortedMap[X, Y] = {
            a ++ b
        }
        implicit def orderedX(x: X): Ordered[X] = new Ordered[X] { def compare(that: X) = 0 }
    }
    

    这里的想法是“隐式”陈述的意思 X s可转换为 Ordered[X] S,然后结合起来就有意义了 分类地图 进入另一 分类地图 而不仅仅是一张地图。

    当我编译时,我得到

    sieversii:scala-2.8.0.Beta1-RC4 scott$ bin/scalac -versionScala compiler version
    2.8.0.Beta1-RC4 -- Copyright 2002-2010, LAMP/EPFL
    
    sieversii:scala-2.8.0.Beta1-RC4 scott$ bin/scalac ViewBoundExample.scala
    ViewBoundExample.scala:8: error: type arguments [ViewBoundExample.X] do not
        conform to method ordered's type parameter bounds [A <: scala.math.Ordered[A]]
            a ++ b
              ^
    one error found
    

    如果类型参数绑定是 [A <% scala.math.Ordered[A]] ,而不是 [A <: scala.math.Ordered[A]] . 不幸的是,我甚至不知道“有序”的方法在哪里!有人能帮我找到它吗?

    如果不能做到这一点,我该怎么做才能产生二人联盟呢? 分类地图 S?如果我移除联合收割机的返回类型(或将其更改为 Map )一切都很好——但是我不能依赖于退货的处理!

    2 回复  |  直到 14 年前
        1
  •  5
  •   Flaviu Cipcigan    15 年前

    目前,您使用的是 scala.collection.SortedMap 性状,其 ++ 方法继承自 MapLike 特质。因此,您可以看到以下行为:

    scala> import scala.collection.SortedMap
    import scala.collection.SortedMap
    
    scala> val a = SortedMap(1->2, 3->4)
    a: scala.collection.SortedMap[Int,Int] = Map(1 -> 2, 3 -> 4)
    
    scala> val b = SortedMap(2->3, 4->5)
    b: scala.collection.SortedMap[Int,Int] = Map(2 -> 3, 4 -> 5)
    
    scala> a ++ b
    res0: scala.collection.Map[Int,Int] = Map(1 -> 2, 2 -> 3, 3 -> 4, 4 -> 5)
    
    scala> b ++ a
    res1: scala.collection.Map[Int,Int] = Map(1 -> 2, 2 -> 3, 3 -> 4, 4 -> 5)
    

    返回结果的类型 ++ 是一个 Map[Int, Int] ,因为这是唯一有意义的类型 ++ A方法 似地图的 要返回的对象。似乎 ++ 保留的排序属性 SortedMap 我想是因为 ++ 使用抽象方法进行连接,这些抽象方法被定义为保持映射的顺序。

    我建议你用 scala.collection.immutable.SortedMap .

    scala> import scala.collection.immutable.SortedMap
    import scala.collection.immutable.SortedMap
    
    scala> val a = SortedMap(1->2, 3->4)
    a: scala.collection.immutable.SortedMap[Int,Int] = Map(1 -> 2, 3 -> 4)
    
    scala> val b = SortedMap(2->3, 4->5)
    b: scala.collection.immutable.SortedMap[Int,Int] = Map(2 -> 3, 4 -> 5)
    
    scala> a ++ b
    res2: scala.collection.immutable.SortedMap[Int,Int] = Map(1 -> 2, 2 -> 3, 3 -> 4, 4 -> 5)
    
    scala> b ++ a
    res3: scala.collection.immutable.SortedMap[Int,Int] = Map(1 -> 2, 2 -> 3, 3 -> 4, 4 -> 5)
    

    本次实施 分类地图 特征表明 ++ 返回 分类地图 .

    现在有几个关于类型界限的问题的答案:

    • Ordered[T] 是一个特征,如果混合在类中,它指定可以使用 < , > , = , >= , <= . 你只需要定义抽象方法 compare(that: T) 哪些回报 -1 对于 this < that , 1 对于 this > that 0 对于 this == that . 然后,所有其他方法都是基于 compare .

    • T <% U 表示scala中绑定的视图。这意味着那种类型 T 不是的子类型 U 或者可以隐式转换为 U 通过作用域中的隐式转换。如果你把代码 <% 但不能 <: 作为 X 不是的子类型 Ordered[X] 但可以隐式转换为 已订购[X] 使用 OrderedX 隐式转换。

    编辑: 关于你的评论。如果您正在使用 scala.collection.immutable.sortedmap ,您仍在编程到接口而不是实现,因为 分类地图 定义为 trait . 你可以把它看作是 scala.collection.sorted地图 提供附加操作(如 ++ 返回一个 分类地图 )以及不可变的性质。这符合scala的哲学——更喜欢不可变——因此我看不出使用不可变的任何问题。 分类地图 . 在这种情况下,你可以保证结果 一定地 因为集合是不可变的,所以不能更改。

    尽管如此,我仍然觉得奇怪的是 scala.collection.sorted地图 不提供 ++ 方法witch返回 分类地图 因此。我所做的所有有限的测试似乎都表明两个连接的结果 scala.collection.sorted地图 S确实生成了一个保存排序属性的映射。

        2
  •  4
  •   Daniel C. Sobral    15 年前

    作为一个初学者,你有没有挑选一个坚硬的坚果来破解斯卡拉!-)

    好的,简短的旅行,不要期望现在就完全理解它。首先,请注意,问题发生在方法中 ++ . 寻找它的定义,我们发现它的特点 MapLike ,接收 Iterator 或A Traversable . 自从 y 是一个 SortedMap ,然后是 可移动的 正在使用的版本。

    在其扩展类型签名中注意到 CanBuildFrom 被通过。它是隐式传递的,所以您通常不需要担心它。然而,要理解发生了什么,这次你要做。

    您可以通过单击canbuildFrom的定义中它出现的位置来定位canbuildFrom。 ++ 或者通过过滤。正如Randall在评论中提到的,scalaDoc页面的左上角有一个未标记的空白字段。你只需点击那里输入,它就会返回你输入的匹配项。

    所以,看看这个特点 坎勃罗德 在scaladoc上选择它。它有大量的子类,每个子类负责构建特定类型的集合。搜索并单击子类 SortedMapCanBuildFrom . 这是您需要生成的对象的类 排序映射 从A 可穿越的 .关于实例构造函数(类的构造函数)的注意事项是,它接收到一个隐式 Ordering 参数。现在我们越来越接近了。

    这次,使用筛选器搜索 排序 . 它的伴生对象(单击小的“o”名称)承载将生成的隐式 排序 作为伴生对象,将检查该类的隐式生成实例或转换。它是在特性内部定义的。 LowPriorityOrderingImplicits ,哪个对象 排序 扩展,查看它,您将看到方法 ordered[A <: Ordered[A]] 将产生 排序 必修的。。。如果没有问题的话,也会生产出来。

    我们可以假定从 X Ordered[X] 这就足够了,就像我之前更仔细地研究这个问题一样。然而,这是对 物体 ordered 期望收到 类型 它是 有序[ X ] . 同时可以转换类型的对象 X 到类型的对象 有序[ X ] , X 本身不是 有序[ X ] ,因此它不能作为参数传递给 命令 .

    另一方面,您可以创建一个隐式 val Ordering[X] ,而不是 def 有序[ X ] 你会解决这个问题的。明确地:

    object ViewBoundExample {
        class X
        def combine[Y](a: SortedMap[X, Y], b: SortedMap[X, Y]): SortedMap[X, Y] = {
            a ++ b
        }
        implicit val orderingX = new Ordering[X] { def compare(x: X, y: X) = 0 }
    }
    

    我认为大多数人对 Ordered / 排序 一定很困惑:为什么要为同样的事情上课?前者延伸 java.lang.Comparable ,而后者延伸 java.util.Comparator . 唉,类型签名 compare 主要区别在于:

    def compare(that: A): Int     // Ordered
    def compare(x: T, y: T): Int  // Ordering
    

    AN的使用 Ordered[A] 两者都需要 A 延伸 有序[ a] 需要一个人能够 修改 或传递一个方法,该方法可以 变成一个 有序[ a] . scala完全可以轻松完成后者,但是 在比较之前转换每个实例。

    另一方面,使用 Ordering[A] 需要创建单个对象,如上面所示。使用它时,只传递两个类型的对象 比较 --在此过程中未创建任何对象。

    因此,需要获得一些性能提升,但scala更倾向于 订购 结束 命令 . 再次查看伴生对象 排序 . 您将注意到其中定义的许多scala类都有几个隐式。你可能还记得我之前提到的一个隐式for类 T 将在 T 正是这样。

    这个 能够 做某事 命令 也。然而,这就是症结所在,这意味着每种方法都支持 排序 命令 会失败的!这是因为scala会寻找一个隐式的方法使其工作,并且会找到两个:一个用于 订购 一个 命令 . 由于无法确定您想要哪一个,scala放弃了一条错误消息。因此,必须做出选择,并且 排序 有更多的事情要做。

    噢,我忘了解释为什么签名没有被定义为 ordered[A <% Ordered[A]] 而不是 已订购[A<:已订购[A] . 我怀疑这样做会导致我之前提到的双重隐式失败,但是我会问真正做了这些事情并且有双重隐式问题的人,这个特定的方法是否有问题。

    推荐文章