Why Does Ruby Only Permit Certain Operator Overloading
Methods are overloadable, those are part of the language syntax.
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: 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>
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 operator-overloading not working properly for a custom class object
Try this instead:
%i(- +).each do |op|
define_method(op) do |v|
@coords = @coords.zip(v.coords).map { |a, b| a.send(op, b) }
self
end
end
You want to zip
the array, so calling coords
on v
makes that work. Also, map
performs the given block and returns the collected results, you were discarding them.
Are you aware that Ruby has a Vector class?
2.1.5 :001 > require 'matrix'
=> true
2.1.5 :002 > a = Vector[1,2,3,4]
=> Vector[1, 2, 3, 4]
2.1.5 :003 > b = Vector[1,1,1,1]
=> Vector[1, 1, 1, 1]
2.1.5 :004 > a - b
=> Vector[0, 1, 2, 3]
2.1.5 :005 > a * 3
=> Vector[3, 6, 9, 12]
What does +@ mean as a method in ruby
Ruby contains a few unary operators, including +
, -
, !
, ~
, &
and *
. As with other operators you can also redefine these. For ~
and !
you can simply just say def ~
and def !
as they don't have a binary counterpart (e.g. you cannot say a!b
).
However for -
and +
there is both a unary, and a binary version (e.g. a+b
and +a
are both valid), so if you want to redefine the unary version you have to use def +@
and def -@
.
Also note that there is a unary version of *
and &
as well, but they have special meanings. For *
it is tied to splatting the array, and for &
it is tied to converting the object to a proc, so if you want to use them you have to redefine to_a
and to_proc
respectively.
Here is a more complete example showing all kinds of the unary operators:
class SmileyString < String
def +@
SmileyString.new(self + " :)")
end
def -@
SmileyString.new(self + " :(")
end
def ~
SmileyString.new(self + " :~")
end
def !
SmileyString.new(self + " :!")
end
def to_proc
Proc.new { |a| SmileyString.new(self + " " + a) }
end
def to_a
[SmileyString.new(":("), self]
end
end
a = SmileyString.new("Hello")
p +a # => "Hello :)"
p ~a # => "Hello :~"
p *a # => [":(", "Hello"]
p !a # => "Hello :!"
p +~a # => "Hello :~ :)"
p *+!-~a # => [":(", "Hello :~ :( :! :)"]
p %w{:) :(}.map &a # => ["Hello :)", "Hello :("]
In your example the Module just simply defines an unary + operator, with a default value of not doing anything with the object (which is a common behaviour for the unary plus, 5
and +5
usually mean the same thing). Mixing in with any class would mean the class immediately gets support for using the unary plus operator, which would do nothing much.
For example (using ruby <=2.2
):
module M
def +@
self
end
end
p +"Hello" # => NoMethodError: undefined method `+@' for "Hello":String
class String
include M
end
p +"Hello" # => "Hello"
Note that in this example you can clearly see from the error message that the +@
method is missing from the class
Note that the above example will be different from Ruby 2.3, as the unary minus and plus are defined for Strings since that version, and they refer to returning a frozen and unfrozen string from the original.
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.
Overriding == equality operator works only in one direction
It's not possible without changing other classes. Basically a == b
equals a.==(b)
. So you need to override ==
operator for the second class if you want to make it work.
Related Topics
Need Help Maximizing 3 Factors in Multiple, Similar Objects and Ordering Appropriately
Ruby Tcpsocket: Find Out How Much Data Is Available
How to Add a Single Backslash Character to a String in Ruby
Disable Rdoc and Ri Generation by Default for Rubygems 1.8.X
How to Add Syntax Highlighting to Sublime Text 2
How to Search Array Through Ransack Gem
Grouped Collection Select Alphabetical Order Rails
How to Deal with App_Key and App_Secret (Dropbox API)
How to Make Ruby's Restclient Gem Respect Content_Type on Post
Finding the Ip Address of a Domain
How to Use Escape Characters in Strings
How to Run All Ruby Scripts with Warnings
Error Installing Rubymine, No Sdk Specified, But It Is Listed
Regex to Validate String Having Only Characters (Not Special Characters), Blank Spaces and Numbers
Xlsx Compressed by Rubyzip Not Readable by Excel
How to Remove Xcode 4.2 and Install 4.1 to Develop Ruby/Rails on Osx Lion
Execjs::Programerror in Welcome#Index Typeerror: Object Doesn't Support This Property or Method