Вопрос

I created this class based on predefined tests

class Roman
  include Comparable
  ROMAN_NUMBERS = { 1000 => "m", 900 => "cm", 500 => "d", 400 => "cd", 
      100 => "c", 90 => "xc", 50 => "l", 40 => "xl", 10 => "x", 9 => "ix", 
      5 => "v", 4 => "iv", 1 => "i" }

  def initialize(number)
    @number = number
  end

  def coerce(other)
    if Integer === other
      [ other, @number ]
    else
      [ Float(other), Float(@number) ]
    end
  end

  def <=>(n)
    @number <=> n
  end

  def -(n)
    Roman.new(@number - n)
  end 

  def to_s
    roman = ''
    ROMAN_NUMBERS.each do |value, letter|
      roman << letter * (@number / value)
      @number = @number % value
    end
    roman
  end
end

but I'm still failing this one

it "should support substraction" do
  (Roman.new(5) - Roman.new(3)).should == 2
  (Roman.new(4) - Roman.new(1)).should == 3
  (Roman.new(4) - 2).should == 2
  res = 6 - Roman.new(1)
  res.should == 5
  (res.kind_of? Roman).should be true
end

The problem is, that res is "kind of" Fixnum. How's that possible? How to make it pass the test?

Это было полезно?

Решение

The problem is that 6 - Roman.new(1) is the same as 6.-(Roman.new(1)) i.e. it is calling the built in subtraction method of Fixnum. Since Fixnum doesn't know about your Roman class, this calls your coerce method.

But you defined coerce to return a Fixnum in this case! The solution is to coerce the other operand, rather than self.

def coerce other
  [self.class.new(other), self]
end

But if you try this with your code, you'll get a stack error! That's because it's now coercing both operands into instances of Roman and it still doesn't know how to subtract them. You should redefine your subtraction method to handle those cases.

attr_reader :number
def - n
  return Roman.new(@number - n.number) if n.is_a? Roman
  Roman.new(@number - n)
end
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top