⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 13 - doing math with roman numbers.rb

📁 O Reilly Ruby Cookbook source code
💻 RB
字号:
class Roman  # These arrays map all distinct substrings of Roman numbers  # to their Arabic equivalents, and vice versa.  @@roman_to_arabic = [['M', 1000], ['CM', 900], ['D', 500], ['CD', 400],     ['C', 100], ['XC', 90], ['L', 50], ['XL', 40], ['X', 10], ['IX', 9],     ['V', 5], ['IV', 4], ['I', 1]]  @@arabic_to_roman = @@roman_to_arabic.collect { |x| x.reverse }.reverse  # The Roman symbol for 5000 (a V with a bar over it) is in neither  # ASCII nor Unicode, so we won't represent numbers larger than 3999.  MAX = 3999  def initialize(number)    if number.respond_to? :to_str      @value = Roman.to_arabic(number)    else      Roman.assert_within_range(number)      @value = number    end  end    # Raise an exception if a number is too large or small to be represented  # as a Roman number.  def Roman.assert_within_range(number)    unless number.between?(1, MAX)      msg = "#{number} can't be represented as a Roman number."      raise RangeError.new(msg)    end  end  #Find the Fixnum value of a string containing a Roman number.  def Roman.to_arabic(s)    value = s    if s.respond_to? :to_str      c = s.dup      value = 0      invalid = ArgumentError.new("Invalid Roman number: #{s}")      value_of_previous_number = MAX+1      value_from_previous_number = 0      @@roman_to_arabic.each_with_index do |(roman, arabic), i|        value_from_this_number = 0        while c.index(roman) == 0          value_from_this_number += arabic          if value_from_this_number >= value_of_previous_number            raise invalid          end          c = c[roman.size..s.size]        end                #This one's a little tricky. We reject numbers like "IVI" and        #"IXV", because they use the subtractive notation and then        #tack on a number that makes the total overshoot the number        #they'd have gotten without using the subtractive        #notation. Those numbers should be V and XIV, respectively.        if i > 2 and @@roman_to_arabic[i-1][0].size > 1 and             value_from_this_number + value_from_previous_number >=             @@roman_to_arabic[i-2][1]          raise invalid        end                value += value_from_this_number        value_from_previous_number = value_from_this_number        value_of_previous_number = arabic        break if c.size == 0      end      raise invalid if c.size > 0    end    return value  end    def to_arabic    @value  end    #Render a Fixnum as a string depiction of a Roman number  def to_roman    value = to_arabic    Roman.assert_within_range(value)    repr = ""    @@arabic_to_roman.reverse_each do |arabic, roman|      num, value = value.divmod(arabic)      puts "Roman #{roman} num #{num}"      repr << roman * num    end    repr  end  #---  # Delegate all methods to the stored integer value. If the result is  # a Integer, transform it into a Roman object. If it's an array  # containing Integers, transform it into an array containing Roman  # objects.  def method_missing(m, *args)    super unless @value.respond_to?(m)    hex_args = args.collect do |arg|       arg.kind_of?(Roman) ? arg.to_int : arg     end    result = @value.send(m, *hex_args)    return result if m == :coerce    begin      case result           when Integer        Roman.new(result)      when Array        result.collect do |element|          element.kind_of?(Integer) ? Roman.new(element) : element        end      else        result      end    rescue RangeError      # Too big or small to fit in a Roman number. Use the Fixnum.      result    end  end#---  def respond_to?(method_name)     super or @value.respond_to? method_name   end  def to_s    to_roman  end  def inspect    to_s  endend#---class Fixnum  def to_roman    Roman.new(self)  endendclass String  def to_roman    Roman.new(self)  endend#---72.to_roman                                    # => LXXII444.to_roman                                   # => CDXLIV1979.to_roman                                  # => MCMLXXIX'MCMXLVIII'.to_roman                           # => MCMXLVIIIRoman.to_arabic('MCMLXXIX')                    # => 1979'MMI'.to_roman.to_arabic                       # => 2001'MMI'.to_roman + 3                             # => MMIV'MCMXLVIII'.to_roman                           # => MCMXLVIII612.to_roman * 3.to_roman                      # => MDCCCXXXVI(612.to_roman * 3).divmod('VII'.to_roman)      # => [CCLXII, II]612.to_roman * 10000                           # => 6120000    # Too big612.to_roman * 0                               # => 0          # Too small'MCMXCIX'.to_roman.succ                        # => MM('I'.to_roman..'X'.to_roman).collect# => [I, II, III, IV, V, VI, VII, VIII, IX, X]#---'IIII'.to_roman# ArgumentError: Invalid Roman number: IIII'IVI'.to_roman# ArgumentError: Invalid Roman number: IVI'IXV'.to_roman# ArgumentError: Invalid Roman number: IXV'MCMM'.to_roman# ArgumentError: Invalid Roman number: MCMM'CIVVM'.to_roman# ArgumentError: Invalid Roman number: CIVVM-10.to_roman# RangeError: -10 can't be represented as a Roman number.50000.to_roman# RangeError: 50000 can't be represented as a Roman number.#---<ol style="list-style-type:lower-roman"><li>Primus</li><li>Secundis</li><li>Tertius</li></ol>#---

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -