Подтвердить что ты не робот

Переопределить метод equals() пары

Этот вопрос ранее был задан в scala -случающем списке рассылки без подтвержденного ответа.

scala> val T = new Pair(1, 2){
override def equals(obj:Any) = obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1}
           }

T: (Int, Int) = (1,2)

scala> T match {
        case (1, 1) => println("matched")
        case _ => println("not matched")
   }

not matched


scala> (1, 1) match {
              case T => println("matched")
              case _ => println("not matched")
          }

not matched

scala> T == (1, 1)
res15: Boolean = true

Я думал, что результат согласования с константой (val) зависит от возвращаемого значения "equals", но результаты показывают, что это не так, тогда каковы критерии?

Кто-то предположил, что case (1, 1) => является шаблоном экстрактора и вместо этого использует Tuple2.unapply. поэтому я пробовал:

scala> Pair.unapply(T)
res1: Option[(Int, Int)] = Some((1,2))

scala> Pair.unapply(T).get == (1, 1)
res2: Boolean = true

Может кто-нибудь объяснить, почему == получить правду, но я не могу заставить их соответствовать?

4b9b3361

Ответ 1

С разрешением # 3888 я могу дать окончательный ответ на этот вопрос.

  • T match { case (1, 1) =>

    Нет, это не имеет никакого отношения к unapply. Как упоминалось в extempore, case (1,1) => является "шаблоном Tuple", псевдоним для "шаблона конструктора" класса case Tuple2, он соответствует только значениям, построенным как Tuple2 (1, 1) или Pair (1, 1).

    Что действительно касается unapply - это "Шаблон экстрактора":

    object Pair {
        val T = new Pair(1,1){
            def unapply(p:(Int, Int)) :Boolean = this._1 == p._1
        }
        def main(args: Array[String]) = {
            (1, 2) match {
                case T() => println("matched")
                case _ => println("not matched")
            }
        } 
    }
    

    Вы получаете "соответствие". Обратите внимание на () в разделе case

  • (1, 1) match { case T => ...

    в соответствии с scala spec, раздел 8.1.5, это "шаблон стабильного идентификатора", case T соответствует любому значению v, так что T == v. Поэтому мы ДОЛЖНЫ получить "соответствие". Результаты "несоответствия" просто вызваны ошибкой в ​​текущей реализации компилятора.

Ответ 2

Проблема с вашим примером заключается в том, что вы переопределяете только метод equals анонимного класса, которым вы определяете свой собственный кортеж. Познакомьтесь с тем, что вы делаете при запуске кода, который вы здесь указали.

val p = new Pair(1, 2) {
override def equals(obj:Any) = {
    obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1
  }
}

Что здесь делает Scala, он создает новый анонимный класс, который расширяет Pair и переопределяет его равные. Таким образом, это эквивалентно запуску следующего кода:

class Foo extends Pair(1,2) {
  override def equals(obj:Any) = {
      obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1
    }
}

val p = new Foo

И вот где вы можете точно увидеть, в чем проблема! Определение equals не является симметричным. p == (1,1) оценивается как true, а (1,1) == p - false! Это связано с тем, что первая эквивалентна p.equals((1,1)), а последняя эквивалентна (1,1).equals(p). В примере, который вы указали, он не работает, потому что объект в этом случае сравнивается с сопоставленным объектом, а не наоборот. Поэтому, как вы указали, Pair.unapply(p).get == (1, 1) оценивается как true, однако (1,1) == Pair.unapply(p).get оценивается как false, и кажется, что последним является тот, который используется при сопоставлении.

Тем не менее, в любом случае создание равенства, которое не является симметричным, действительно очень плохой идеей, поскольку выполнение кода зависит от порядка, в котором вы сравниваете объекты. Кроме того, у равных вы определили дополнительную проблему - это с ошибкой при попытке сравнить ваш p с любым Pair, который не относится к типу (Int, Int). Это связано с тем, что после стирания типа (как JVM реализует дженерики), Pair больше не параметризуется типами его составляющих. Следовательно, (Int, Int) имеет тот же тип, что и (String, String), и, следовательно, следующий код с ошибкой завершится ошибкой:

p == ("foo", "bar")

поскольку Scala попытается применить a (String, String) к (Int, Int).

Если вы хотите реализовать эту функциональность, самое легкое, что вы могли бы сделать, - использовать шаблон моей библиотеки, сутенерство Pair. Однако вы не должны называть свой метод equals. Назовите это чем-то другим, например ~=. Я должен идти сейчас, однако, когда я вернусь, я могу дать вам код для этого. Это довольно легко. Вы должны посмотреть на реализацию equals в паре и удалить часть, которая сравнивает второй аргумент:)

Ответ 3

Я должен сказать, что Scala удивил меня снова. Еще несколько тестов показывают, что результат зависит от контекста работы.

Если вы запустите:

object Pair{
    def main(args:Array[String]) {
        val T = new Pair(1, 2){
            override def equals(obj:Any)= obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1
        }

        (1, 1) match {
            case T => println("matched")
            case _ => println("not matched")
        }
    }

}

вы получаете: соответствие

И если вы запустите:

object Pair extends Application {
    val T = new Pair(1, 2){
        override def equals(obj:Any)= obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1
    }

    (1, 1) match {
        case T => println("matched")
        case _ => println("not matched")
    }
}

вы получаете: не соответствует

Я всегда думал, что нет никакой разницы между кодом в методах main() и кодом в теле объекта, который расширяет черту Application. Действительно странно.

Ответ 4

Серпухи имеют привилегию в шаблоне. Они не ваши повседневные занятия. Это в спецификации, раздел 8.1.7, "Tuple Patterns".

Когда вы говорите

(1, 1) match { case T ...

Тогда на (1, 1) вызывается равенство, которое, конечно же, не говорит, не равно.

Когда вы говорите

T match { case (1, 1) => ...

Затем ваш метод equals игнорируется из-за шаблона кортежа, а T._1 сравнивается с 1 и T._2 с 1, и снова он не соответствует.