Ruby operator overloading
I believe the answer to "ruby operator overloading question" addresses both your points by using is_a?
and coerce
.
With regards to your first point. The normal approach in Ruby is to use respond_to?
where possible, rather than checking for type. If for some reason you specifically need to check for type, then using is_a?
is the correct way.
ruby operator overloading question
Using coerce is a MUCH better approach than monkey-patching a core class:
class Vec3
attr_accessor :x,:y,:z
def *(a)
if a.is_a?(Numeric) #multiply by scalar
return Vec3.new(@x*a, @y*a, @z*a)
elsif a.is_a?(Vec3) #dot product
return @x*a.x + @y*a.y + @z*a.z
end
end
def coerce(other)
return self, other
end
end
if you define v as v = Vec3.new
then the following will work: v * 5
and 5 * v
The first element returned by coerce (self) becomes the new receiver for the operation, and the second element (other) becomes the parameter, so 5 * v
is exactly equivalent to v * 5
Ruby overload + operator
Overloaded operator is resolved based on the class of the first operand, so if you wanted to overload the addition of simple integers, something like that should work:
class Fixnum
def +(other)
return self * other
end
end
I do not recommend you to actually do this, btw.
list of ruby operators that can be overridden/implemented
Here's a table of the Ruby operators.
The ones that are methods and overloadable are:
[ ] [ ]= Element reference, element set
** Exponentiation
! ~ + - Not, complement, unary plus and minus (method names for the last two are +@ and -@)
* / % Multiply, divide, and modulo
+ - Plus and minus
>> << Right and left shift
& Bitwise `and'
^ | Bitwise exclusive `or' and regular `or'
<= < > >= Comparison operators
<=> == === != =~ !~ Equality and pattern match operators (!= and !~ may not be defined as methods)
The table was from the 2001 Pickaxe book, but that's the same table as in the Ruby 1.9 Pickaxe book -- no reason to believe that this set of infix operators will ever change.
Ruby method for +=
The +=
operator is not associated to any method, it is just syntactic sugar, when you write a += b
the Ruby interpreter transform it to a = a + b
, the same is for a.b += c
that is transformed to a.b = a.b + c
. Thus you just have to define the methods x=
and x
as you need:
class Plane
def initialize
@moved = 0
@x = 0
end
attr_reader :x
def x=(x)
@x = x
@moved += 1
end
def to_s
"moved #{@moved} times, current x is #{@x}"
end
end
plane = Plane.new
plane.x += 5
plane.x += 10
puts plane.to_s
# => moved 2 times, current x is 15
Ruby: overload operator behaviour for some cases only
Both approaches posted here so far are a legacy Rails way, which is plain wrong. It relies on the fact that the class has no method called plus
and nobody will reopen the class to create a method called plus
. Otherwise things will go mad.
The correct solution is Module#prepend
:
Integer.prepend(Module.new do
def + other
case other
when Fixnum then special_behaviour
else super(other)
end
end
end)
question on overriding + operator in ruby
You should not mutate the object when implementing +
operator. Instead return a new Point Object:
class Point
attr_accessor :x, :y
def initialize(x,y)
@x, @y = x, y
end
def +(other)
Point.new(@x + other.x, @y + other.y)
end
def to_s
"(#{@x}, #{@y})"
end
end
ruby-1.8.7-p302:
> p1 = Point.new(1,2)
=> #<Point:0x10031f870 @y=2, @x=1>
> p2 = Point.new(3, 4)
=> #<Point:0x1001bb718 @y=4, @x=3>
> p1 + p2
=> #<Point:0x1001a44c8 @y=6, @x=4>
> p3 = p1 + p2
=> #<Point:0x1001911e8 @y=6, @x=4>
> p3
=> #<Point:0x1001911e8 @y=6, @x=4>
> p1 += p2
=> #<Point:0x1001877b0 @y=6, @x=4>
> p1
=> #<Point:0x1001877b0 @y=6, @x=4>
How do you overload the operator in Ruby?
You need to do that from within a class. Like this:
class Whatever
attr_accessor :roles
def initialize
@roles = []
end
end
You can't really have a <<roles
method. You'd have to have an accessor for roles
that supports the <<
operator.
EDIT: I've updated the code. Now you can see how the <<
operator should be overloaded, but you can also do what the roles<<
part. Here's a small snippet of it's usage:
w = Whatever.new
w << "overload for object called"
# and overloads for the roles array
w.roles << "first role"
w.roles << "second role"
Related Topics
Ruby on Rails 4 - What Authentication Gem to Use
How to Install a Gem Globally Without Sudo Using Rbenv
Error Installing Gem: Couldn't Reserve Space for Cygwin's Heap, Win32 Error 487
Cheat Sheet for All Design Patterns Implemented in Ruby
Controlling Tor Client with Ruby
Is Assignment in a Conditional Clause Good Ruby Style
How to Get a Substring from Position N to the Last Char in Ruby
Where to Insert Rack::Deflater in the Rack
Machinist VS Factorygirl - Pros and Cons
Problem When Installing Ruby 2.2.9 on MAC Big Sur M1
Marking an Unused Block Variable
How to Do Case-Insensitive Order in Rails with Postgresql
Differencebetween 'Size' and 'Length' Methods
Ruby on Rails - Paperclip and Dynamic Parameters
Connecting to Oracle Db Using Ruby
How to Get Rspec to Run All Tests Nested Under a Folder