class MultiSet[A <: Ordered[A] ](val tm: TreeMap[A,Int]) { ... }
现在我想使用这个框架创建一个MultiSet [Int].特别是,我想要一个方法,它将采用Vector [Int]并生成一个TreeMap [Int,Int],我可以用它来创建一个MultiSet [Int].
我写了下面的vectorToTreeMap,它编译时无怨无悔.
def vectorToTreeMap[A <: Ordered[A]](elements: Vector[A]): TreeMap[A,Int] = elements.foldLeft(new TreeMap[A,Int]())((tm,e) => tm.updated(e,tm.getOrElse(e,0) + 1))
但是,当我尝试
val tm: TreeMap[Int,Int] = vectorToTreeMap(Vector(1,2,3))
我收到编译器投诉说Int不符合A<:Ordered [A].在此上下文中创建TreeMap [Int,Int]需要什么? (我想要更一般的情况,因为MultiSet [A]并不总是MultiSet [Int].) 我也试过A<:scala.math.Ordered [A]和A< ;: Ordering [A]但没有更好的结果. (我承认我不理解三种可能性之间的差异以及在这种情况下是否重要.) 谢谢你的帮助.
解决方法
有两种方法可以解决您的问题:
查看范围:
第一种方法是将约束<:更改为视图绑定<%.
def vectorToTreeMap[A <% Ordered[A]](elements: Vector[A]): TreeMap[A,0) + 1))
A< ;:Ordered [A]意味着方法vectorToTreeMap仅为直接实现Ordered [A]的类型定义,它排除了Int.
<%Ordered [A]意味着为所有“可被视为”实现Ordered [A]的类型定义了vectorToTreeMap方法,其中包括Int,因为存在从Int到Ordered [Int]定义的隐式转换:
scala> implicitly[Int => Ordered[Int]] res7: Int => Ordered[Int] = <function1>
输入类
第二种方法是不要求类型A的任何(直接或间接)继承关系,但只要求存在一种方法来订购类型A的实例.
基本上,您总是需要一个能够从向量创建TreeMap的排序,但是为了避免在每次调用方法时都必须传递它,您将使排序成为隐式参数.
def vectorToTreeMap[A](elements: Vector[A])(implicit ordering:Ordering[A]): TreeMap[A,0) + 1))
事实证明,对于所有java原始类型以及String都存在Ordering [A]的实例,正如您可以在scala REPL中使用隐式方法看到的:
scala> implicitly[Ordering[Int]] res8: Ordering[Int] = scala.math.Ordering$Int$@5b748182
Scala甚至可以导出复合类型的排序.例如,如果你有一个元组,其中存在每个元素类型的排序,scala将自动提供元组类型的排序:
scala> implicitly[Ordering[(Int,Int)]] res9: Ordering[(Int,Int)] = scala.math.Ordering$$anon$11@66d51003
使用所谓类型类的第二种方法更灵活.例如,如果您想要一个普通旧的int树,但是具有相反的顺序,那么您所要做的就是直接或作为隐式val提供反向int排序.
这种方法在惯用语中也很常见.所以甚至有特殊的语法:
def vectorToTreeMap[A : Ordering](elements: Vector[A]): TreeMap[A,Int] = ???
相当于
def vectorToTreeMap[A](elements: Vector[A])(implicit ordering:Ordering[A]): TreeMap[A,Int] = ???
它基本上意味着您希望方法vectorToTreeMap仅针对存在排序的类型定义,但您不关心为排序命名.即使使用短语法,您也可以将vectorToTreeMap与隐式求解的Ordering [A]一起使用,或者明确地传递Ordering [A].
第二种方法有两大优点:
>它允许您为不“拥有”的类型定义功能.
>它允许你解耦某些方面的行为,例如从类型本身排序,而使用继承方法,您将行为耦合到类型.例如,您可以为Sting提供正常的Ordering和caseInsensitiveOrdering.但是如果你让String从Ordered扩展,你必须决定一个排序行为.
这就是为什么在scala集合本身中使用第二种方法来提供TreeMap的排序.
编辑:这是一个为没有一个类型的类型提供排序的示例:
scala> case class Person(name:String,surname:String) defined class Person scala> implicitly[Ordering[Person]] <console>:10: error: No implicit Ordering defined for Person. implicitly[Ordering[Person]] ^
案例类没有自动定义的排序.但我们可以很容易地定义一个:
scala> :paste // Entering paste mode (ctrl-D to finish) case class Person(name:String,surname:String) object Person { // just convert to a tuple,which is ordered by the individual elements val nameSurnameOrdering : Ordering[Person] = Ordering.by(p => (p.name,p.surname)) // make the nameSurnameOrdering the default that is in scope unless something else is specified implicit def defaultOrdering = nameSurnameOrdering } // Exiting paste mode,now interpreting. defined class Person defined module Person scala> implicitly[Ordering[Person]] res1: Ordering[Person] = scala.math.Ordering$$anon$9@50148190