我想做这样的事情:
class Foo extends Ordered[Foo] {
val x
val y
val z
.
.
.
.
def compare(that: Foo) = {
val c0 = this.length compareTo that.length // primary comparison
lazy val c1 = this.x compareTo that.x // secondary comparison
lazy val c2 = this.y.size compareTo that.y.size // tertiary comparison
lazy val c3 = this.z.head compareTo that.z.head // final tie breaker
if (c0 != 0) c0 else if (c1 != 0) c1 else if (c2 != 0) c2 else if (c3 != 0) c3 else c4
}
}
我想知道是否有更清洁的方式来写这种东西。我期待像Ordering.multipleBy(ordering: Ordered[A]*)
签名之类的东西,它采用可比较的变量并选择第一个非零。
答案 0 :(得分:30)
通常最好使用Ordering
代替Ordered
。 Ordering
是一个类型类,比Ordered
更灵活(如果只是因为Ordered
必须由要比较的类型实现,而Ordering
则可以在Ordering
之外定义)。要为您的类型定义自然排序(默认Ordering
实例),您只需在配对对象中定义隐式排序值。
所以,序言就足够了。好的是,当使用object Foo {
implicit val FooOrdering = Ordering.by{ foo: Foo =>
(foo.length, foo.x, foo.y, foo.z)
}
}
时,你想要做的事情非常简单,因为元组有一个隐式排序(假设元组元素本身有一个排序)`:
Ordering
此外,还有一个隐式转换,它将具有Ordered
类型类实例的任何值转换为Ordered.orderingToOrdered
值(请参阅Foo
),因此我们没有什么特别的事情要做自动将Ordered[Foo]
的任何实例传递给期望Ordering.by
的函数
更新:关于您的新问题:
略有关联 - 有没有办法组成排序?
一种方法是使用基于val byXOrdering = Ordering.by{ foo: Foo => foo.x }
val byYOrdering = Ordering.by{ foo: Foo => foo.y }
val byZOrdering = Ordering.by{ foo: Foo => foo.z }
// Compose byXOrdering and byYOrdering:
val byXThenYOrdering = Ordering.by{ foo: Foo => (foo, foo) }(Ordering.Tuple2(byXOrdering, byYOrdering))
// Compose byXOrdering and byYOrdering and byZOrdering:
val byXThenYThenZOrdering = Ordering.by{ foo: Foo => (foo, foo, foo) }(Ordering.Tuple3(byXOrdering, byYOrdering, byZOrdering))
的大部分相同的技术并转换为元组,但明确地将排序传递给撰写:
final class CompositeOrdering[T]( val ord1: Ordering[T], val ord2: Ordering[T] ) extends Ordering[T] {
def compare( x: T, y: T ) = {
val comp = ord1.compare( x, y )
if ( comp != 0 ) comp else ord2.compare( x, y )
}
}
object CompositeOrdering {
def apply[T]( orderings: Ordering[T] * ) = orderings reduceLeft (_ orElse _)
}
implicit class OrderingOps[T]( val ord: Ordering[T] ) extends AnyVal {
def orElse( ord2: Ordering[T] ) = new CompositeOrdering[T]( ord, ord2 )
}
但它相对“吵”。 我只使用标准库找不到更好的东西,所以我实际建议使用我们自己的帮手:
val byXOrdering = Ordering.by{ foo: Foo => foo.x }
val byYOrdering = Ordering.by{ foo: Foo => foo.y }
val byZOrdering = Ordering.by{ foo: Foo => foo.z }
// Compose byXOrdering and byYOrdering:
val byXThenYOrdering = byXOrdering orElse byYOrdering
// Compose byXOrdering and byYOrdering and byZOrdering:
val byXThenYThenZOrdering = byXOrdering orElse byYOrdering orElse byZOrdering
可以这样使用:
// Compose byXOrdering and byYOrdering:
val byXThenYOrdering = CompositeOrdering(byXOrdering, byYOrdering)
// Compose byXOrdering and byYOrdering and byZOrdering:
val byXThenYThenZOrdering = CompositeOrdering(byXOrdering, byYOrdering, byZOrdering)
甚至更简单,像这样:
CompositeOrdering.apply
Ordering.multipleBy
基本上就是您在问题中所称的{{1}}。
答案 1 :(得分:5)
如果你想要最大的速度 - 不是你要求的,我知道! - 而且仍然不错,你可以
def compare(that: Foo): Int = {
this.length compareTo that.length match { case 0 =>; case c => return c }
this.x compareTo that.x match { case 0 =>; case c => return c }
this.y.size compareTo that.y.size match { case 0 =>; case c => return c }
this.z.head compareTo that.z.head match { case 0 =>; case c => return c }
0
}
还有各种不错的基于收藏的解决方案和其他解决方案,我将留给其他人解释。 (注意所有样板文件,并观察所有你真正需要知道的是_.length
在每种情况下......例如,它激励compareBy
。)
答案 2 :(得分:3)
我能想到的最好的是:
def compare(that: Foo) = multiCompare(
this.length compareTo that.length // primary comparison
this.x compareTo that.x, // secondary comparison
this.y.size compareTo that.y.size, // tertiary comparison
this.z.head compareTo that.z.head, // final tie breaker
)
def multiCompare(c: ( => Int)*) = c find {_ != 0} getOrElse 0
答案 3 :(得分:-1)
我使用这种语法是因为它是自我记录的:
val compositeOrdering: Ordering[Foo] =
Comparator.comparing[Foo, CustomType](_.x, new ExplicitOrderingForCustomType)
.thenComparingDouble(_.y)
.thenComparingInt(_.z)
.compare
它正在构造一个复合 java.util.Comparator
,然后将其 compare
方法转换为 scala.math.Ordering