Question on Learning Ruby the Hard Way

Hi, I have a question on Exercise 43 of Learning R. the Hard Way.

Specifically, I’m wondering if someone to talk me through what’s
happening below.

def play()
next_room = @start

while true
  puts "\n--------"
  room = method(next_room)
  next_room = room.call()
end

end

I can see that variable next_room is set to instance variable @start.
But how does this “while” loop work? I’m also not familiar with how
“method” and “call” are both used here, as well as the : used in return
(e.g. return :central_corridor) below.

class Game

def initialize(start)
@quips = [
“You died. You kinda suck at this.”,
“Nice job, you died …jackass.”,
“Such a luser.”,
“I have a small puppy that’s better at this.”
]
@start = start
end

def prompt()
print "> "
end

def central_corridor()
puts “The Gothons of Planet Percal #25 have invaded your ship and
destroyed”
puts “your entire crew. You are the last surviving member and your
last”
puts “mission is to get the neutron destruct bomb from the Weapons
Armory,”
puts "put it in the bridge, and blow the ship up after getting into
an "
puts “escape pod.”
puts “\n”
puts “You’re running down the central corridor to the Weapons Armory
when”
puts “a Gothon jumps out, red scaly skin, dark grimy teeth, and evil
clown costume”
puts “flowing around his hate filled body. He’s blocking the door
to the”
puts “Armory and about to pull a weapon to blast you.”

prompt()
action = gets.chomp()

if action == "shoot!"
  puts "Quick on the draw you yank out your blaster and fire it at

the Gothon."
puts “His clown costume is flowing and moving around his body,
which throws”
puts “off your aim. Your laser hits his costume but misses him
entirely. This”
puts “completely ruins his new costume his mother bought
him, which”
puts “makes him fly into an insane rage and blast you repeatedly
in the face until”
puts “you are dead. Then he eats you.”
return :death

elsif action == "dodge!"
  puts "Like a world class boxer you dodge, weave, slip and slide

right"
puts “as the Gothon’s blaster cranks a laser past your head.”
puts “In the middle of your artful dodge your foot slips and you”
puts “bang your head on the metal wall and pass out.”
puts “You wake up shortly after only to die as the Gothon stomps
on”
puts “your head and eats you.”
return :death

elsif action == "tell a joke"
  puts "Lucky for you they made you learn Gothon insults in the

academy."
puts “You tell the one Gothon joke you know:”
puts “Lbhe zbgure vf fb sng, jura fur fvgf nebhaq gur ubhfr, fur
fvgf nebhaq gur ubhfr.”
puts “The Gothon stops, tries not to laugh, then busts out
laughing and can’t move.”
puts “While he’s laughing you run up and shoot him square in the
head”
puts “putting him down, then jump through the Weapon Armory door.”
return :laser_weapon_armory

else
  puts "DOES NOT COMPUTE!"
  return :central_corridor
end

end

def play()
next_room = @start

while true
  puts "\n--------"
  room = method(next_room)
  next_room = room.call()
end

end

a_game = Game.new(:central_corridor)
a_game.play()

On Sun, 1 Jul 2012 21:19:53 +0900
Michael S. [email protected] wrote:

  room = method(next_room)
  next_room = room.call()
end

end

I can see that variable next_room is set to instance variable @start.
But how does this “while” loop work? I’m also not familiar with how
“method” and “call” are both used here, as well as the : used in
return (e.g. return :central_corridor) below.

Inside loop you are calling method instance method, which returns a
Method instance, in your case it’s central_corridor, then you simply
calling it with call() same as next_room = central_corridor() at
least in the first time in that case. :))

See details here:


Sincerely yours,
Aleksey V. Zapparov A.K.A. ixti
FSF Member #7118
Mobile Phone: +34 677 990 688
Homepage: http://www.ixti.net
JID: [email protected]

*Origin: Happy Hacking!

On Mon, 2 Jul 2012 01:38:30 +0900
“Jan E.” [email protected] wrote:

a = A.new
f = a.method(:f) #create a Method object for method “f”
p f
f.call() # call Method object (without arguments)

See
Class: Object (Ruby 1.9.3)
Class: Method (Ruby 1.9.3)

Michael, please notice that this Method object is bounded to the object
it was initiated from:

class Foo
def initialize
@i = 0
end

def bar
@i += 1
end
end

o = Foo.new
m = o.method :bar

o.bar # => 1
m.call # => 2
m.call # => 3
o.bar # => 4


Sincerely yours,
Aleksey V. Zapparov A.K.A. ixti
FSF Member #7118
Mobile Phone: +34 677 990 688
Homepage: http://www.ixti.net
JID: [email protected]

*Origin: Happy Hacking!

Thank you for the help! I’ll try to wrap my head around these concepts
now.

Hi,

For the next time, please attach the code rather than post it. The
forum software inserts a line break after every 80 characters or so,
which destroys the whole formatting.

It also seems you’ve silently left out several methods. Of course you
can shorten the code, but then you should tell us. Well, for this
particular example we can more or less guess which methods are missing
and what they look like. But if this was a more complicated class, it
would really be annoying to get an error message and having to figure
out what’s missing.

To the actual questions:

Michael S. wrote in post #1066882:

def play()
next_room = @start

while true
  puts "\n--------"
  room = method(next_room)
  next_room = room.call()
end

end

I can see that variable next_room is set to instance variable @start.
But how does this “while” loop work? I’m also not familiar with how
“method” and “call” are both used here, as well as the : used in return
(e.g. return :central_corridor) below.

The while loop is an endless loop. You probably know how a while loop
works in general. In this case the condition is always true, so the loop
will run forever.

However, it would have been better to use an explicit endless loop like
Kernel#loop:

loop do

body

end

Calling “method” with a method name as a Symbol creates a Method object
for this method. This object can then be passed around and called at any
time (by calling the “call” method):

class A
def f
puts “f called”
end
end

a = A.new
f = a.method(:f) #create a Method object for method “f”
p f
f.call() # call Method object (without arguments)

See

The Game class uses this to call methods dynamically: Every method like
“central_corridor” or “laser_weapon_armory” returns the name of the
method which should be called next (as a Symbol). This allows the user
to control the flow of the game. For example, if he’s in the central
corridor and types in “shoot!”, the “death” method will be called. If he
types in “tell a joke”, the “laser_weapon_armory” method will be called
(he continues to the next room).

However, a better way to achieve this is by using Object#send. This
method takes a method name as a Symbol together with optional arguments
and then calls this method.

So you can replace

room = method(next_room)
next_room = room.call()

with

next_room = send next_room

See

It may also be a good idea to use “public_send” instead, which will only
call public methods:

One more quick question.

I think I understand the creation of a method object:

room = method(next_room)

But why does the next line say:

next_room = room.call()

Instead of just calling the method object “room” which we just created
like this:

room.call()

On Mon, 2 Jul 2012 03:58:41 +0900
Michael S. [email protected] wrote:

next_room = room.call()

Instead of just calling the method object “room” which we just
created like this:

room.call()

Because room in this case is not a method, but an object.


Sincerely yours,
Aleksey V. Zapparov A.K.A. ixti
FSF Member #7118
Mobile Phone: +34 677 990 688
Homepage: http://www.ixti.net
JID: [email protected]

*Origin: Happy Hacking!

Michael S. wrote in post #1066942:

Instead of just calling the method object “room” which we just created
like this:

room.call()

You need the next room, which is returned by this method.

The whole point of the loop is to go to the first room and get the next
room, go to this room and get the next room, go to this room and get the
next room …

When you use “send” as suggested, the structure becomes clearer:

next_room = @start
loop do
puts “\n--------”
next_room = send next_room
end

So the program is actually an infinite chain of “send” calls:

send(send(send(…send(@start))))