Reputation: 2683
I am studying Ruby and can't figure this out. I have an exercise where I have to add a method into a Library class which contains games. Each game is an instance of a class Game. So the solution is the following:
class Library
attr_accessor :games
def initialize(games)
self.games = games
end
def has_game?(search_game)
for game in games
return true if game == search_game
end
false
end
def add_game(game)
@games << game
end
end
I can't understand how <<
works in this case. Is this a bitwise left shift? Does Library class just assumes that games
is an array, I believe I can pass anything to the Library class when I am initialising, single game or an array of games?
Upvotes: 3
Views: 2643
Reputation: 110685
When you have:
@games << game
<<
is actually a method. If it is a method, you ask, why isn't it written in the normal way:
@games.<<(game)
? You could, in fact, write it that way and it would work fine. Many Ruby methods have names that are symbols. A few others are +
, -
, **
, &
, ||
and %
. Ruby knows you'd prefer writing 2+3
instead of 2.+(3)
, so she let's you do the former (and then quietly converts it to the latter). This accommodation is often referred to as "syntactic sugar".
<<
is one of @games
' methods (and game
is <<
's argument), because @games
is the receiver of the method <<
(technically :<<
). Historically, it's called the "receiver" because with OOP you "send" the method to the receiver.
To say that <<
is a method of @games
means that <<
is an instance method of @games
's class. Thus, we have:
@games.methods.include?(:<<) #=> true
@games.class.instance_methods.include?(:<<) #=> true
I expect @games
is an instance of the class Array
. Array
's instance methods are listed here. For example:
@games = [1,2,3]
@games.class #=> [1,2,3].class => Array
@games << 4 #=> [1,2,3,4]
@games.<<(5) #=> [1,2,3,4,5]
On the other hand, suppose @games
were an instance of Fixnum
. For example:
@games = 7
@games.class #=> 7.class => Fixnum
which in binary looks like this:
@games.to_s(2) #=> "111"
Then:
@games << 2 #=> 28
28.to_s(2) #=> "11100"
because <<
is an instance method of the class Fixnum.
As a third example, suppose @games
were a hash:
@games = { :a => 1 }
@games.class #=> { :a => 1 }.class => Hash
Then:
@games << { :b => 2 }
#=> NoMethodError: undefined method `<<' for {:a=>1}:Hash
The problem is clear from the error message. The class Hash has no instance method <<
.
One last thing: consider the method Object#send. Recall that, at the outset, I said that methods are sent to receivers. Instead of writing:
@games = [1,2,3]
@games << 4 #=> [1,2,3,4]
you could write:
@games.send(:<<, 4) #=> [1, 2, 3, 4]
This is how you should think of methods and receivers. Because send
is an instance method of the class Object
, and all objects inherit Object
's instance methods, we see that send
is "sending" a method (:<<
, which can alternatively be expressed as a string, "<<"
) and its arguments (here just 4
) to the receiver.
There are times, incidentally, when you must use send
to invoke a method. For one, send
works with private
and protected
methods. For another, you can use send
when you want to invoke a method dynamically, where the name of the method is the value of a variable.
In sum, to understand what method
does in:
receiver.method(*args)
look for the doc for the instance method method
in receiver
's class. If you Google "ruby array", for example, the first hit will likely be the docs for the class Array
.
Upvotes: 7
Reputation: 7482
It's not a bitwise left shift, it's "shovel" operator. You, probably, know that in Ruby operators are implemented as methods, i.e. when you write
1 + 1
what is actually going on:
1.+(1)
The same is true for "shovel" (<<
) operator, it's just a method on Array
class, that appends its arguments to the end of the array and returns the array itself, so it can be chained:
>> arr = []
=> []
>> arr << 1
=> [1]
>> arr
=> [1]
>> arr << 2 << 3
=> [1, 2, 3]
Upvotes: 3
Reputation: 26444
It looks like @games
is an array. The shift operator for an array adds an element to the end of the array, similar to array#push
.
ary << obj → ary
Append—Pushes the given object on to the end of this array. This expression returns the array itself, so several appends may be chained together.
Upvotes: 1