(我正在使用Scala nightlies,并在2.8.0b1 RC4中看到相同的行为。我是Scala的新手。)
我有两个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
可以转换为Ordered[X]
s,然后将SortedMap
组合成另一个SortedMap
是有道理的,而不仅仅是一张地图。
当我编译时,我得到了
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]]
,我的问题似乎就会消失。不幸的是,我甚至无法找出“命令”生活的方法!任何人都可以帮我追踪它吗?
如果不这样做,我打算如何制作两个SortedMap
s的联合?如果我删除了返回类型的组合(或将其更改为Map
),一切正常 - 但是我不能依赖返回的排序!
答案 0 :(得分:5)
目前,您使用的是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]
,因为这是++
对象的MapLike
方法返回的唯一类型。似乎++
保留了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)
SortedMap
特征的此实现声明了++
方法,该方法返回SortedMap
。
现在,您对类型界限的问题有几个答案:
Ordered[T]
是一个特征,如果在一个类中混合,它指定可以使用<
,>
,=
,{{1 },>=
。您只需要定义抽象方法<=
,它为compare(that: T)
返回-1
,为this < that
返回1
,为this > that
返回0
。然后基于this == that
。
compare
表示Scala中绑定的视图。这意味着类型T <% U
或者是T
的子类型,或者可以通过范围中的隐式转换隐式转换为U
。如果您放置U
但不放<%
,则代码有效,因为<:
不是X
的子类型,但可以使用{{隐式转换为Ordered[X]
1}}隐式转换。
修改:关于您的评论。如果您使用的是Ordered[X]
,那么您仍在编程接口而不是实现,因为不可变的OrderedX
被定义为scala.collection.immutable.SortedMap
。您可以将其视为SortedMap
的更专业特征,它提供其他操作(如返回trait
的{{1}})和不可变属性。这符合Scala哲学 - 更喜欢不变性 - 因此我没有看到使用不可变scala.collection.SortedMap
的任何问题。在这种情况下,您可以保证结果将明确地进行排序,并且由于集合是不可变的,因此无法更改。
尽管如此,我仍然觉得奇怪的是,++
没有提供SortedMap
方法,因此会返回SortedMap
。我所做的所有有限测试似乎都表明,两个scala.collection.SortedMap
的串联结果确实产生了一个保存已排序属性的映射。
答案 1 :(得分:4)
作为Scala的初学者,你选择了一个难以破解的坚果! : - )
好的,简短的游览,不要指望现在完全理解它。首先,请注意问题发生在方法++
上。在搜索其定义时,我们在特征MapLike
处找到它,接收Iterator
或Traversable
。由于y
是SortedMap
,因此它是使用的Traversable
版本。
请注意,在其广泛的类型签名中,传递了CanBuildFrom
。它是隐式传递的,因此您通常不需要担心它。但是,要了解发生了什么,这次你会这样做。
您可以通过点击++
定义中的CanBuildFrom或过滤来找到它。正如Randall在评论中提到的那样,scaladoc页面的左上角有一个未标记的空白字段。你只需要点击那里并键入,它就会返回你输入的内容的匹配项。
因此,在ScalaDoc上查找特征CanBuildFrom
并选择它。它有大量的子类,每个子类负责构建特定类型的集合。搜索并单击子类SortedMapCanBuildFrom
。这是从SortedMap
生成Traversable
所需的对象类。请注意它接收隐式Ordering
参数的实例构造函数(类的构造函数)。现在我们越来越近了。
这一次,使用过滤器过滤器搜索Ordering
。其伴随对象(单击名称中的小“o”)将隐藏一个将生成Ordering
的隐式对象,因为将检查协同对象是否为该类生成实例或转换。它在特征LowPriorityOrderingImplicits
内定义,对象Ordering
扩展,查看它会看到方法ordered[A <: Ordered[A]]
,它将产生所需的Ordering
...如果只是没有问题,或者会产生它。
有人可能会认为从X
到Ordered[X]
的隐式转换就足够了,就像我在仔细研究之前所做的那样。但是,这是对象的转换,而ordered
期望接收类型,它是Ordered[X]
的子类型。虽然可以将X
类型的对象转换为Ordered[X]
类型的对象,但X
本身不是Ordered[X]
的子类型,因此无法传递作为ordered
的参数。
另一方面,您可以创建隐式val
Ordering[X]
,而不是def
Ordered[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
/ Ordering
的初步反应必定是困惑之一:为什么要为相同的事情提供课程?前者扩展java.lang.Comparable
,而后者扩展java.util.Comparator
。唉,compare
的类型签名几乎总结了主要区别:
def compare(that: A): Int // Ordered
def compare(x: T, y: T): Int // Ordering
使用Ordered[A]
要求A
延长Ordered[A]
,这需要一个人能够修改 A
'定义,或传递一个可以将A
转换为Ordered[A]
的方法。 Scala完全能够轻松地完成后者,但是在比较之前你有来转换每个实例。
另一方面,使用Ordering[A]
需要创建单个对象,如上所述。使用它时,只需将A
类型的两个对象传递给compare
- 在此过程中不会创建任何对象。
因此,有一些性能提升,但Scala偏好Ordering
优于Ordered
的原因更为重要。再次查看Ordering
的伴随对象。您会注意到,在那里定义的许多Scala类有几个含义。您可能还记得我之前提到的,T
的伴随对象内部将会搜索隐含的类T
,而这正是发生的事情。
此也可以为Ordered
完成。但是,这是关键点,这意味着支持Ordering
和Ordered
的每个方法都会失败!那是因为Scala会寻找隐含的功能,并且会找到两个:Ordering
一个,Ordered
一个。由于无法确定您想要的是什么,Scala会放弃错误消息。因此,必须做出选择,Ordering
还有更多的事情要做。
Duh,我忘了解释为什么签名没有定义为ordered[A <% Ordered[A]]
,而不是ordered[A <: Ordered[A]]
。我怀疑这样做会导致我之前提到过的双重隐含失败,但是我会问那些真正做过这个问题的人,并且这个特殊方法是否有问题会产生双重隐含问题。