为了补充Peter的回答:在Scala中我们有两个特征:Ordering[T]和Ordered[A]。你应该在不同的情况下使用它们。
Ordered[A]
适用于您实施的课程可自然订购并且该订单是唯一的订单。
例子:
class Fraction(val numerator: Int, val denominator: Int) extends Ordered[Fraction]
{
def compare(that: Fraction) = {
(this.numerator * that.denominator) compare (this.denominator * that.numerator)
}
}
Ordering[T]
是情况下,当你希望有不同的方式来订购的东西。这样,定义订单的策略就可以从订购的类中分离出来。
举一个例子,我将借用彼得Person
:
case class Person(name: String, age: Int)
object PersonNameOrdering extends Ordering[Person]
{
def compare(x: Person, y: Person) = x.name compare y.name
}
注意,既然PersonNameOrdering
没有任何实例字段,它是所有封装定义两个Person
的订单的逻辑。因此我把它做成了object
而不是class
。
为了减少您可以使用Ordering.on定义样板的Ordering
:
val personAgeOrdering: Ordering[Person] = Ordering.on[Person](_.age)
现在到了有趣的部分:如何使用这些东西。
在您的原始代码Foo[A].quantity
间接定义了一种方法来订购您的A
的。现在,使其惯用斯卡拉让我们使用Ordering[A]
代替,并重新命名quantity
到ord
:
trait Foo[A] {
def baz(x1: A, x2: A, ord: Ordering[A]) = {
import ord._
if (x1 > x2) "first is greater"
else "first is less or equal"
}
}
有几件事情要注意这里:
import ord._
允许使用中缀表示法比较,即x1 > x2
VS ord.gt(x1, x2)
baz
现在通过订购进行参数设置,因此您可以动态选择如何按个别情况订购x1
和x2
,个案基础:
foo.baz(person1, person2, PersonNameOrdering)
foo.baz(person1, person2, personAgeOrdering)
事实上,ord
现在是一个明确的参数有时是不方便的:你可能不希望将它传递明确的时候,而当你想要做可能会有某些情况下,所以。牵涉到救援!
def baz(x1: A, x2: A) = {
def inner(implicit ord: Ordering[A]) = {
import ord._
if (x1 > x2) "first is greater"
else "first is less or equal"
}
inner
}
请注意implicit
关键字。它被用来告诉编译器从隐含的范围的情况下,你没有提供它明确地得出参数:
// put an Int value to the implicit scope
implicit val myInt: Int = 5
def printAnInt(implicit x: Int) = { println(x) }
// explicitly pass the parameter
printAnInt(10) // would print "10"
// let the compiler infer the parameter from the implicit scope
printAnInt // would print "5"
你可能想了解where does Scala look for implicits。
另一件需要注意的是需要嵌套函数。您不能编写def baz(x1: A, x2: A, implicit ord: Ordering[A])
- 这不会编译,因为implicit
关键字适用于整个参数列表。
为了应对这个小问题,baz
被重写得如此笨拙。
rewritting这种形式竟然是如此普遍,一个漂亮的语法糖中引入了它 - 多个参数列表:
def baz(x1: A, x2: A)(implicit ord: Ordering[A]) = {
import ord._
if (x1 > x2) "first is greater"
else "first is less or equal"
}
需要一个隐含的一个类型参数化的也相当普遍所以上面的代码可以更加糖被改写 - 方面势必:
def baz[A: Ordering](x1: A, x2: A) = {
val ord = implicitly[Ordering[A]]
import ord._
if (x1 > x2) "first is greater"
else "first is less or equal"
}
请谨记的所有这些转换功能不过是句法糖的应用。所以所有版本都是完全一样的,编译器会将每个版本解除为相同的字节码。
总括:
- 从
quantity
功能到Ordering[A]
类提取A
排序逻辑;
- 将
Ordering[A]
的实例置于隐式范围或根据您的需要明确传递排序;
- 挑选语法糖的“风味”
baz
:无糖/嵌套函数,多参数列表或上下文绑定。
UPD
要回答原来的问题: “为什么不把它编译?”让我从关于中缀比较运算符如何在Scala中工作的一点点偏离开始。
考虑下面的代码:
val x: Int = 1
val y: Int = 2
val greater: Boolean = x > y
这里到底发生了什么。斯卡拉本身没有中缀运算符,而中缀运算符只是单参数方法调用的语法糖。所以内部上面的代码转换到这一点:
val greater: Boolean = x.>(y)
现在棘手的问题:Int没有对自己的>
方法。在ScalaDoc页面上选择,继承,并检查此方法是否列在标题为“由隐式转换intWrapper从Int到RichInt继承”的组中。
所以内部编译器这个(当然,除了性能方面的原因,有上堆额外的对象没有实际实例):
val greater: Boolean = (new RichInt(x)).>(y)
如果我们进行的RichInt ScalaDoc和再订货的方法继承原来,>方法实际上来自Ordered!
让我们重写整个块,以使其更清晰实际发生的事情:
val x: Int = 1
val y: Int = 2
val richX: RichInt = new RichInt(x)
val xOrdered: Ordered[Int] = richX
val greater: Boolean = xOrdered.>(y)
重写应该强调的各类参与对比参数:在右边的左边和Int
Ordered[Int]
。请参阅>文档以进行确认。
现在让我们回到原来的代码,然后重写以同样的方式来突出类型:
trait Foo[A] {
def quality[B](x: A): Ordered[B]
def baz(x1: A, x2: A) = {
// some algorithm work here, and then
val x1Ordered: Ordered[B] = quality(x1)
val x2Ordered: Ordered[B] = quality(x2)
if (x1Ordered > x2Ordered) {
// some stuff
}
}
}
正如你所看到的类型并不一致:他们是Ordered[B]
和Ordered[B]
,而>
比较为了工作,他们应该分别是Ordered[B]
和B
。
问题是你在哪里得到这个B
把它放在正确的位置?对我来说,B
似乎在这方面实际上与A
相同。以下是我想出了:
trait Foo[A] {
def quality(x: A): Ordered[A]
def baz(x1: A, x2: A) = {
// some algorithm work here, and then
if (quality(x1) > x2) {
"x1 is greater"
} else {
"x1 is less or equal"
}
}
}
case class Cargo(weight: Int)
class CargoFooImpl extends Foo[Cargo] {
override def quality(x: Cargo): Ordered[Cargo] = new Ordered[Cargo] {
override def compare(that: Cargo): Int = x.weight compare that.weight
}
}
这种方法的缺点是,它不是很明显:中quality
实施过于冗长,quality(x1) > x2
不是对称的。
底线:
- ,如果你想要的代码是惯用的Scala去
Ordering[T]
- ,如果你不想惹implicits和其他斯卡拉魔术实施质量
quality(x: A): Double
全部为A
s; Double
s很好,通用性足以进行比较和排序。
不是扩展'Ordered [A]',你可以确保存在'Ordering [A]'。 – Kolmar
我想我不完全理解:)我怎样才能确保存在'订购[A]'? – avanwieringen