Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на языке Ruby.docx
Скачиваний:
18
Добавлен:
06.09.2019
Размер:
1.74 Mб
Скачать

11.1.7. Проверка объектов на равенство

Все животные равны, но некоторые равнее других.

Джордж Оруэлл, «Скотный двор»

При написании своих классов желательно, чтобы семантика типичных операций была такой же, как у встроенных в Ruby классов. Например, если объекты класса можно упорядочивать, то имеет смысл реализовать метод <=> и подмешать модуль Comparable. Тогда к объектам вашего класса будут применимы все обычные операции сравнения.

Однако картина перестает быть такой однозначной, когда дело доходит до проверки объектов на равенство. В Ruby объекты реализуют пять разных методов для этой операции. И в ваших классах придется реализовать хотя бы некоторые из них, поэтому рассмотрим этот вопрос подробнее.

Самым главным является метод equal? (унаследованный от класса Object); он возвращает true, если вызывающий объект и параметр имеют один и тот же идентификатор объекта. Это фундаментальный аспект семантики объектов, поэтому переопределять его не следует.

Самым распространенным способом проверки на равенство является старый добрый оператор ==, который сравнивает значения вызывающего объекта и аргумента. Наверно, интуитивно это наиболее очевидный способ.

Следующим в шкале абстракции стоит метод eql? — тоже часть класса Object. (На самом деле метод eql? реализован в модуле Kernel, который подмешивается в Object.) Как и оператор ==, этот метод сравнивает значения вызывающего объекта и аргумента, но несколько более строго. Например, разные числовые объекты при сравнении с помощью == приводятся к общему типу, но метод eql? никогда не считает объекты разных типов равными.

flag1 = (1 == 1.0)    # true

flag2 = (1.eql?(1.0)) # false

Метод eql? существует по одной-единственной причине: для сравнения значений ключей хэширования. Если вы хотите переопределить стандартное поведение Ruby при использовании объектов в качестве ключей хэша, то переопределите методы eql? и hash.

Любой объект реализует еще два метода сравнения. Метод === применяется для сравнения проверяемого значения в предложении case с каждым селектором: selector===target. Хотя правило на первый взгляд кажется сложным, на практике оно делает предложения case в Ruby интуитивно очевидными. Например, можно выполнить ветвление по классу объекта:

case an_object

 when String

  puts "Это строка."

 when Numeric

  puts "Это число."

 else

  puts "Это что-то совсем другое."

end

Эта конструкция работает, потому что в классе Module реализован метод ===, проверяющий, принадлежит ли параметр тому же классу, что вызывающий объект (или одному из его предков). Поэтому, если an_object — это строка «cat», выражение string === an_object окажется истинным и будет выбрана первая ветвь в предложении case.

Наконец, в Ruby реализован оператор сопоставления с образцом =~. Традиционно он применяется для сопоставления строки с регулярным выражением. Но если вы найдете ему применение в других классах, то можете переопределить.

У операторов == и =~ есть противоположные формы: != и !~ соответственно. Внутри они реализованы путем обращения значения основной формы. Это означает, что если, например, вы реализовали метод ==, то метод != получаете задаром.