Ответ 1
Часто лучше использовать 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
в своем вопросе.