KirKon753
KirKon753

Reputation: 25

Ruby overloading operators with severals parameters

I have a class MoneyBox with two fields (wallet and cent). I need to overload operations +, -, and * with these fields. How to correctly implement the overloading of these operators with two parameters?

class MoneyBox
  attr_accessor :wallet, :cent

  def initialize(wallet, cent)
  @wallet = wallet  
  @cent = cent 
   
  end

  def + (obj, obj_cent)
    self.wallet = self.wallet + obj.wallet
    self.cent = self.cent + obj_cent.cent
    return self.wallet, self.cent 
  end

  def - (obj, obj_cent) 
    self.wallet = self.wallet - obj.wallet
    self.cent = self.cent - obj_cent.cent
    return self.wallet, self.cent 
  end

  def * (obj,obj_cent) 
    self.wallet = self.wallet * obj.wallet
    self.cent = self.cent * obj_cent
    return self.wallet, self.cent 
  end

end

It should be something like this:

Cash1 = MoneyBox.new(500, 30)
Cash2 = MoneyBox.new(100, 15)

puts " D1 = #{D1 = (Cash1-(Cash2*2))}" # 500,30 - 100,15*2 = 300,0

Upvotes: 0

Views: 59

Answers (3)

Stefan
Stefan

Reputation: 114178

You already have a class that encapsulates wallet / cent pairs and the operations are also performed pair-wise. Why not take advantage of it and make +, - and * take a (single) MoneyBox instance as their argument and return the result as another MoneyBox instance, e.g.: (arithmetic operators shouldn't modify their operands)

class MoneyBox
  attr_accessor :wallet, :cent

  def initialize(wallet, cent)
    @wallet = wallet
    @cent = cent
  end

  def +(other)
    MoneyBox.new(wallet + other.wallet, cent + other.cent)
  end

  def -(other)
    MoneyBox.new(wallet - other.wallet, cent - other.cent)
  end

  def *(other)
    MoneyBox.new(wallet * other.wallet, cent * other.cent)
  end

  def to_s
    "#{wallet},#{cent}"
  end
end

Example usage:

cash1 = MoneyBox.new(500, 30)
cash2 = MoneyBox.new(100, 15)

puts "#{cash1} + #{cash2} = #{cash1 + cash2}"
# 500,30 + 100,15 = 600,45

puts "#{cash1} - #{cash2} = #{cash1 - cash2}"
# 500,30 - 100,15 = 400,15

puts "#{cash1} * #{cash2} = #{cash1 * cash2}"
# 500,30 * 100,15 = 50000,45

To multiply both wallet and cents by 2 you'd use a MoneyBox.new(2, 2) instance:

puts "#{cash1} - #{cash2} * 2 = #{cash1 - cash2 * MoneyBox.new(2, 2)}"
# 500,30 - 100,15 * 2 = 300,0

Note that operator precedence still applies, so the result is evaluated as cash1 - (cash2 * Money.new(2, 2)).

If you want to multiply by integers directly, i.e. without explicitly creating that MoneyBox.new(2, 2) instance, you could move that logic into * by adding a conditional, e.g:

  def *(other)
    case other
    when Integer
      MoneyBox.new(wallet * other, cent * other)
    when MoneyBox
      MoneyBox.new(wallet * other.wallet, cent * other.cent)
    else
      raise ArgumentError, "expected Integer or MoneyBox, got #{other.class}"
    end
  end

Which gives you:

cash1 = MoneyBox.new(500, 30)
cash2 = MoneyBox.new(100, 15)

puts "#{cash1} - #{cash2} * 2 = #{cash1 - cash2 * 2}"
# 500,30 - 100,15 * 2 = 300,0

Note that this only defines MoneyBox#* and doesn't alter Integer#*, so 2 * cash2 does not work by default:

2 * cash2
# TypeError: MoneyBox can't be coerced into Integer

That's because you're calling * on 2 and Integer doesn't know how to deal with a MoneyBox instance. This could be fixed by implementing coerce in MoneyBox: (something that only works for numerics)

  def coerce(other)
    [self, other]
  end

Which effectively turns 2 * cash2 into cash2 * 2.


BTW if you always call * with an integer and there's no need to actually multiply two MoneyBox instances, it would of course get much simpler:

  def *(factor)
    MoneyBox.new(wallet * factor, cent * factor)
  end

Upvotes: 1

user1934428
user1934428

Reputation: 22217

Your example usage does not match the method definition. You want to use your objects in the way to write expressions like i.e. Cash2 * 2). Since Cash2 is a constant of class MoneyBox, the definition for the multiplication by a scalar would be something like

def * (factor)
  self.class.new(wallet, cent * factor)
end

This is a shortcut of

def * (factor)
  MoneyBox.new(self.wallet, self.cent * factor)
end

Upvotes: 1

max pleaner
max pleaner

Reputation: 26768

You can't use the typical x + y syntax if you're going to overload the operator to take two arguments.

Instead, you have to use .+, see the following:

class Foo
  def initialize(val)
    @val = val
  end
   
  def +(a,b)
    @val + a + b
  end
end

foo = Foo.new(1)

foo.+(2,3)
# => 6

Upvotes: 1

Related Questions