20

私はこのようなことをしたい:

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]*)署名のようなものを期待しています。

4

5 に答える 5

34

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に渡すことができるようにするために特別なことは何もありません。FooOrdered[Foo]


更新:あなたの新しい質問について:

少し関連しています - 注文を作成する方法はありますか?

それを行う1つの方法は、タプルに基づい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.applyOrdering.multipleByは基本的にあなたが質問で呼んだものです。

于 2013-02-04T21:58:13.483 に答える
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、 a を動機付けます。)compareBy

于 2013-02-04T21:37:39.837 に答える
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
于 2013-02-04T21:48:56.813 に答える
-1

自己文書化されているため、この構文を使用します。

  val compositeOrdering: Ordering[Foo] =
    Comparator.comparing[Foo, CustomType](_.x, new ExplicitOrderingForCustomType)
    .thenComparingDouble(_.y)
    .thenComparingInt(_.z)
    .compare

コンポジットjava.util.Comparatorを構築し、そのcompareメソッドをキャストしていますscala.math.Ordering

于 2021-02-11T05:59:30.053 に答える