我正在构建MultiSet[A]
并使用TreeMap[A, Int]
来跟踪元素。
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]
但没有更好的结果。 (我承认我不理解三种可能性之间的差异以及在这种情况下是否重要。)
感谢您的帮助。
答案 0 :(得分:3)
问题是 Int 是java int 的别名,它不实现Ordered [Int]。怎么可能呢,因为java甚至不知道Ordered [T]特征存在。
有两种方法可以解决您的问题:
查看边界:
第一种方法是将约束&lt;:更改为绑定&lt;%的视图。
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))
A&lt ;:Ordered [A] 表示方法vectorToTreeMap仅为直接实现Ordered [A]的类型定义,不包括Int。
A&lt;%Ordered [A] 表示方法vectorToTreeMap是为所有“可被视为”实现Ordered [A]的类型定义的,其中包括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, Int] =
elements.foldLeft(new TreeMap[A, Int]())((tm, e) => tm.updated(e, tm.getOrElse(e, 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
使用所谓类型类的第二种方法更灵活。例如,如果您想要一个普通旧的整数树,但是具有相反的顺序,那么您所要做的就是直接或作为隐式val提供反向int排序。
这种方法在惯用scala中也很常见。所以甚至还有特殊的语法:
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