6

Is there a way to make Ruby able to do something like this?

class Plane
  @moved = 0
  @x = 0
  def x+=(v) # this is error
    @x += v
    @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
Kokizzu
  • 24,974
  • 37
  • 137
  • 233
  • 4
    Just rename your method to something else? – oldergod May 29 '13 at 05:11
  • yep, i guess so.. since += cannot be overloaded (i know this just now) from http://stackoverflow.com/questions/3331962/list-of-ruby-operators-that-can-be-overloaded – Kokizzu May 29 '13 at 05:13
  • 2
    It is confusing to use the same variable names `@moved` and `@x` for the class and its instances. – sawa May 29 '13 at 06:18

2 Answers2

6
  1. You cannot override compound assignment operators in Ruby. The assignments are handled internally. Instead of +=, you should override +. plane.a += b is the same as plane.a = plane.a + b or plane.a=(plane.a.+(b)). Thus you should also override a= in Plane.
  2. When you write plane.x += 5, the + message is send to plane.x, not plane. So you should override + method in the class of x, not Plane.
  3. When you refer to @variable, you should pay attention to what the current self is. In class Plane; @variable; end, @variable refers to the instance variable of the class. That's different with the one in class Plane; def initialize; @variable; end; end, which is instance variable of the class's instances. So you can put the initialization part into initialize method.
  4. Operator overriding should be treated carefully. Sometimes it is productive and expressive, but sometimes it isn't. Here I think it's better to define a method (e.g. fly) for plane rather than using some operator.
class Plane
  def initialize
    @x = 0
    @moved = 0
  end
  def fly(v)
    @x += v
    @moved += 1
  end
  def to_s
    "moved #{@moved} times, current x is #{@x}"
  end
end

plane = Plane.new
plane.fly(5)
plane.fly(10)
puts plane.to_s
Arie Xiao
  • 13,909
  • 3
  • 31
  • 30
4

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
toro2k
  • 19,020
  • 7
  • 64
  • 71
  • `a += b` is called an *abbreviated assignment*. When Ruby's parser sees that the first thing she does is convert it to `a = a + b`. Ruby has 13 operators (mostly methods) that can be expressed as abbreviated assignments. All have the form `o=`, where `o` is an operator and `a o= b` expands to `a = a o b`. Those operators are `+`,`-`,`*`, `/`, `%`, `**`, `&&`, `||`, `&`, `|`, `^`, `<<` and `>>`. (Source: The Ruby Programming Language, 1st ed., by Flanagan and Matsumoto, p 96.) – Cary Swoveland Jun 29 '17 at 05:38