Книга: Программирование на языке Ruby
6.2.8. Нестандартные диапазоны
6.2.8. Нестандартные диапазоны
Рассмотрим пример диапазона, состоящего из произвольных объектов. В листинге 6.2 приведен класс для работы с римскими числами.
Листинг 6.2. Класс для работы с римскими числами
class Roman
include Comparable
I,IV,V,IX,X,XL,L,XC,C,CD,D,CM,M =
1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000
Values = %w[M CM D CD С XC L XL X IX V IV I]
def Roman.encode(value)
return "" if self == 0
str = ""
Values.each do |letters|
rnum = const_get(letters)
if value >= rnum
return(letters + str=encode(value-rnum))
end
end
str
end
def Roman.decode(rvalue)
sum = 0
letters = rvalue.split('')
letters.each_with_index do |letter,i|
this = const_get(letter)
that = const_get(letters[i+1]) rescue 0
op = that > this ? :- : :+
sum = sum.send(op,this)
end
sum
end
def initialize(value)
case value
when String
@roman = value
@decimal = Roman.decode(@roman)
when Symbol
@roman = value.to_s
@decimal = Roman.decode(@roman)
when Numeric
@decimal = value
@roman = Roman.encode(@decimal)
end
end
def to_i
@decimal
end
def to_s
@roman
end
def succ
Roman.new(@decima1 +1)
end
def <=>(other)
self.to_i <=> other.to_i
end
end
def Roman(val)
Roman.new(val)
end
Сначала несколько слов о самом классе. Его конструктору можно передать строку, символ (представляющий число, записанное римскими цифрами) или Fixnum
(число, записанное обычными арабскими цифрами). Внутри выполняется преобразование и сохраняются обе формы. Имеется вспомогательный метод Roman
, это просто сокращенная запись вызова Roman.new
. Методы класса encode
и decode
занимаются преобразованием из арабской формы в римскую и наоборот.
Для простоты я опустил контроль данных. Кроме того, предполагается, что римские цифры представлены прописными буквами.
Метод to_i
, конечно же, возвращает десятичное значение, a to_s
— число, записанное римскими цифрами. Метод succ
возвращает следующее римское число: например, Roman(:IV).succ
вернет Roman(:V)
.
Оператор сравнения сравнивает десятичные эквиваленты. Мы включили с помощью директивы include
модуль Comparable
, чтобы получить доступ к операторам «меньше» и «больше» (реализация которых опирается на наличие метода сравнения <=>
).
Обратите внимание на использование символов в следующем фрагменте:
op = that > this ? :- : :+
sum = sum.send(op,this)
Здесь мы решаем, какую будем выполнять операцию (она обозначается символом): сложение или вычитание. Это не более чем краткий способ выразить следующую идею:
if that > this
sum -= this
else
sum += this
end
Второй вариант длиннее, зато более понятен.
Поскольку в этом классе есть метод succ
и полный набор операторов сравнения, его можно использовать для конструирования диапазонов. Пример:
require 'roman'
y1 = Roman(:MCMLXVI)
y2 = Roman(:MMIX)
range = y1..y2 # 1966..2009
range.each {|x| puts x} # Выводятся 44 строки.
epoch = Roman(:MCMLXX)
range.include?(epoch) # true
doomsday = Roman(2038)
range.include?(doomsday) # false
Roman(:V) == Roman(:IV).succ # true
Roman(:MCM) < Roman(:MM) # true
- 6.2. Диапазоны
- 6.2.1. Открытые и замкнутые диапазоны
- 6.2.6. Обратные диапазоны
- Нестандартные рекламные носители
- Нестандартные инструменты HR-брендинга (диджитал, мобильные технологии, квесты и т. п.)
- Некоторые нестандартные методы исследований
- Нестандартные форматы рекламы
- Нестандартные применения
- 16.1.4. Нестандартные свойства
- Совет 25. Изучите нестандартные хэшированные контейнеры
- 2.12. Новый стандарт проектов КСО: максимальная включенность сотрудников, нестандартные решения
- Нестандартные вопросы, которые можно использовать во время общения с клиентом