Главная Обратная связь

Дисциплины:






Листинг 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

Заключение

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

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

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





sdamzavas.net - 2019 год. Все права принадлежат их авторам! В случае нарушение авторского права, обращайтесь по форме обратной связи...