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.3. Заключение

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

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату