Предположим, например, что мы хотим игнорировать английские артикли <=>
(он вызывается из методов <
, <=
, >
и >=
). В листинге 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
Теперь все строки будут сравниваться без учета регистра. И при всех операциях сортировки, которые определены в терминах метода <=>
, регистр тоже не будет учитываться.
2.8. Разбиение строки на лексемы
Метод split
разбивает строку на части и возвращает массив лексем. Ему передаются два параметра: разделитель и максимальное число полей (целое).
По умолчанию разделителем является пробел, а точнее, значение специальной переменной $;
или ее англоязычного эквивалента $FIELD_SEPARATOR
. Если же первым параметром задана некоторая строка, то она и будет использоваться в качестве разделителя лексем.
s1 = 'Была темная грозовая ночь.'
words = s1.split # ['Была', 'темная', 'грозовая', 'ночь]
s2 = 'яблоки, груши, персики'
list = s2.split(', ') # ['яблоки', 'груши', 'персики']
s3 = 'львы и тигры и медведи'
zoo = s3.split(/ и /) # ['львы', 'тигры', 'медведи']
Второй параметр ограничивает число возвращаемых полей, при этом действуют следующие правила:
1. Если параметр опущен, то пустые поля в конце отбрасываются.
2. Если параметр — положительное число, то будет возвращено не более указанного числа полей (если необходимо, весь «хвост» строки помещается в последнее поле). Пустые поля в конце сохраняются.
3. Если параметр — отрицательное число, то количество возвращаемых полей не ограничено, а пустые поля в конце сохраняются.
Ниже приведены примеры:
str = 'alpha,beta,gamma,,'
list1 = str.split(',') # ['alpha','beta','gamma']
list2 = str.split(',',2) # ['alpha', 'beta,gamma,,']
list3 = str.split(',',4) # ['alpha', 'beta', 'gamma', ',']
list4 = str.split(',',8) # ['alpha', 'beta', 'gamma', '', '')
list5 = str.split(',',-1) # ['alpha', 'beta', 'gamma', '', '']
Для сопоставления строки с регулярным выражением или с другой строкой служит метод scan
:
str = 'I am a leaf on the wind...'
# Строка интерпретируется буквально, а не как регулярное выражение.
arr = str.scan('а') # ['а','а','а']
# При сопоставлении с регулярным выражением возвращаются все соответствия.