订单和TreeMap

时间:2014-02-14 07:41:47

标签: scala

我正在构建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]但没有更好的结果。 (我承认我不理解三种可能性之间的差异以及在这种情况下是否重要。)

感谢您的帮助。

1 个答案:

答案 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
相关问题