Reputation: 25
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
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
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
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