Книга: Программирование на языке Ruby

2.7. Специализированное сравнение строк

2.7. Специализированное сравнение строк

В язык Ruby уже встроен механизм сравнения строк: строки сравниваются в привычном лексикографическом порядке (то есть на основе упорядочения, присущего данному набору символов). Но при желании можно задать собственные правила сравнения любой сложности.

Предположим, например, что мы хотим игнорировать английские артикли a, an и the, если они встречаются в начале строки, а также не обращать внимания на большинство знаков препинания. Для этого следует переопределить встроенный метод <=> (он вызывается из методов <, <=, > и >=). В листинге 2.1 показано, как это сделать.

Листинг 2.1. Специализированное сравнение строк

class String
 alias old_compare <=>
 def <=>(other)
  a = self.dup
  b = other.dup
  # Удалить знаки препинания.
  a.gsub!(/[,.?!:;]/, "")
  b.gsub!(/[,.?!:;]/, "")
  # Удалить артикли из начала строки.
  a.gsub!(/^(a |an | the )/i, "")
  b.gsub!(/^(a |an | the )/i, "")
  # Удалить начальные и хвостовые пробелы.
  a.strip!
  b.strip!
  # Вызвать старый метод <=>.
  # a.old_compare(b)
 end
end
title1 = "Calling All Cars"
title2 = "The Call of the Wild"
# При стандартном сравнении было бы напечатано "yes".
if title1 < title2
 puts "yes"
else
 puts "no" # А теперь печатается "no".
end

Обратите внимание, что мы «сохранили» старый метод <=> с помощью ключевого слова alias и в конце вызвали его. Если бы мы вместо этого воспользовались методом <, то был бы вызван новый метод <=>, что привело бы к бесконечной рекурсии и в конечном счете к аварийному завершению программы.

Отметим также, что оператор == не вызывает метод <=> (принадлежащий классу-примеси Comparable). Это означает, что для специализированной проверки строк на равенство пришлось бы отдельно переопределить метод ==. Но в рассмотренном случае в этом нет необходимости.

Допустим, что мы хотим сравнивать строки без учета регистра. Для этого есть встроенный метод casecmp; надо лишь добиться, чтобы он вызывался при сравнении. Вот как это можно сделать:

class String
 def <=>(other)
  casecmp(other)
 end
end

Есть и более простой способ:

class String
 alias <=> casecmp(other)
end

Но это не все. Надо еще переопределить оператор ==, чтобы он вел себя точно так же:

class String
 def ==(other)
  casecmp(other) == 0
 end
end

Теперь все строки будут сравниваться без учета регистра. И при всех операциях сортировки, которые определены в терминах метода <=>, регистр тоже не будет учитываться.

Оглавление книги

Оглавление статьи/книги

Генерация: 0.037. Запросов К БД/Cache: 0 / 0
поделиться
Вверх Вниз