web-dev-qa-db-ja.com

不変のSet比較メソッドで使用されるカスタムの等価演算を定義するにはどうすればよいですか

クラスSet [MyClass]の不変のSetがあり、Setメソッドのintersectとdiffを使用したいが、デフォルトのオブジェクト等価テストではなく、カスタムのequalsメソッドを使用して等価をテストしたい

==演算子をオーバーライドしようとしましたが、使用されていません。

前もって感謝します。

編集:

IntersectメソッドはGenSetLikeの具象値メンバーです

仕様: http://www.scala-lang.org/api/current/scala/collection/GenSetLike.html src: https://lampsvn.epfl.ch/trac/ scala/browser/scala/tags/R_2_9_1_final/src // library/scala/collection/GenSetLike.scala#L1

def intersect(that: GenSet[A]): Repr = this filter that

したがって、交差はフィルターメソッドを使用して行われます。

さらに別の編集:

フィルターはTraversableLikeで定義されています

仕様: http://www.scala-lang.org/api/current/scala/collection/TraversableLike.html

src: https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/collection/TraversableLike.scala#L1

def filter(p: A => Boolean): Repr = {
  val b = newBuilder
      for (x <- this) 
        if (p(x)) b += x
      b.result
}

私にとって不明確なのは、述語なしで呼び出されたときに使用されるものです、p。これは暗黙のパラメータではありません。

43
Kareem

equalsとhashCodeは、定義しない場合にのみ、caseクラスで自動的に提供されます。

_case class MyClass(val name: String) {
  override def equals(o: Any) = o match {
    case that: MyClass => that.name.equalsIgnoreCase(this.name)
    case _ => false
  }
  override def hashCode = name.toUpperCase.hashCode
}

Set(MyClass("xx"), MyClass("XY"), MyClass("xX"))
res1: scala.collection.immutable.Set[MyClass] = Set(MyClass(xx), MyClass(XY))
_

参照の等価性が必要な場合でも、equalsとhashCodeを書き込んで自動生成を防止し、AnyRefからバージョンを呼び出します

_  override def equals(o: Any) = super.equals(o)
  override def hashCode = super.hashCode
_

それと:

_Set(MyClass("x"), MyClass("x"))
res2: scala.collection.immutable.Set[MyClass] = Set(MyClass(x), MyClass(x))
_

AnyRefから==(o: Any)をオーバーライドすることはできません。AnyRefはシールされており、常にequalsを呼び出します。新しい(オーバーロードされた)==(m: MyClass)を定義しようとした場合、それはSetが呼び出すものではないため、ここでは役に立たず、一般に非常に危険です。

filterの呼び出しに関しては、それが機能する理由は、_Set[A]_が_Function[A, Boolean]_であることです。そして、はい、equalsが使用されます。関数の実装(apply)はcontainsの同義語であり、Setのほとんどの実装は_==_ in contains(SortedSetは代わりにOrderingを使用します)。そして_==_はequalsを呼び出します。


注:MyClassをサブクラス化する場合、最初のequalsの実装は迅速でダーティであり、おそらく不適切です。もしそうなら、少なくとも型の等価性(_this.getClass == that.getClass_)をチェックするか、canEqualメソッドをより適切に定義する必要があります(Daniel Sobralによる このブログ を読むことができます)

50
Didier Dupont

.hashCodeもオーバーライドする必要があります。 .equals.hashCodeの安価な事前チェックとしてよく使用されるため、これは.equalsをオーバーライドする場合にほとんど常に当てはまります。等しい2つのオブジェクトは、同一のハッシュコードを持つ必要があります。デフォルトのhashCodeがカスタムの等価性に関してこのプロパティを尊重しないオブジェクトを使用していると思います、そしてSet実装はハッシュコードに基づいて仮定を行っています)。

Scala Any.equalsおよびAny.hashCodeのドキュメント]を参照してください。 http://www.scala-lang.org/api/rc/scala/Any。 html

8
Ben

この回答は、ユーザー定義の等価性を持つカスタムの可変セットを示しています 。内部ストアをVectorに置き換え、操作ごとに自分自身の変更されたコピーを返すことで、不変にすることができます

1
Ben Hutchison

「==を直接オーバーライドすることはできません。Anyクラスの最終メソッドとして定義されているためです。つまり、Scalaは、==をAnyクラスで次のように定義されているかのように扱います。

    final def == (that: Any): Boolean =
      if (null eq this) {null eq that} else {this equals that}

"Scalaのプログラミング、第2版から

0
t. fochtman