I am doing the beginning ruby book and this is what I have for my code, which gives the following error:
in 'show_current_description': undefined method 'full_description' for nil:NilClass (NoMethodError)
The code is as follows. Any help is appreciated. Thanks!
class Dungeon
attr_accessor :player
def initialize(player_name,start_location)
@player=Player.new(p开发者_StackOverflow社区layer_name,start_location)
puts @player.location
@rooms=[]
show_current_description
end
def show_current_description
@rm=find_room_in_dungeon(@player.location)
@rm.full_description
end
def find_room_in_dungeon(reference)
@rooms.detect{|room| room.reference == reference; puts room.full_description}
end
def add_room(reference,name,description,connections)
@rooms << Room.new(reference,name,description,connections)
end
Player=Struct.new(:name,:location)
class Room
attr_accessor :reference, :name, :description, :connections
def initialize(reference,name,description,connections)
@reference=reference
@name=name
@description=description
@connections=connections
end
def full_description
"You are in " + @description
end
end
end
d=Dungeon.new("Good Man",:small_cave)
d.add_room(:small_cave,"Small Cave","This is a small claustrophobic cave", {:east => :largecave})
d.add_room(:large_cave,"Large Cave","This is a large cavernous cave", {:west => :smallcave})
puts d.player.name
d.show_current_description
The error that you posted means that this line...
@rm.full_description
...is trying to call full_description
on an object that has no such method, because the @rm
object is nil
. From that we can deduce that the previous line...
@rm=find_room_in_dungeon(@player.location)
...set @rm
to nil
as the result of the find_room_in_dungeon
method. If we look at that method...
@rooms.detect{|room| room.reference == reference; puts room.full_description}
...we see one problem. The detect
method uses the return value of the block to figure out if a room should be used or not. However, the puts
method (which is the last in the block, and hence the return value of the block) always returns nil
. Hence, @rooms.detect
will never find anything. So let's fix that by removing the puts
altogether:
@rooms.detect{|room| room.reference == reference }
That should help, but it didn't solve the problem. We're still getting nil
from this method. Let's look deeper.
First, let's validate our assumptions about what the precondition for this method is. Before that changed line, we add p @rooms, reference
to the find_room_in_dungeon
method. Running the code now, we see this output:
[]
:small_cave
Ah-ha! So that's the problem. We're looking for the small_cave
room, but our list of rooms is empty! We know that we're calling d.add_room
at the bottom of the script...why isn't it working?
Let's see where we are when this code breaks. Here's the full backtrace from the error message:
C:/tmp.rb:13:in `show_current_description': undefined method `full_description' for nil:NilClass (NoMethodError)
from C:/Users/gkistner.NVIDIA.COM/Desktop/tmp.rb:8:in `initialize'
from C:/Users/gkistner.NVIDIA.COM/Desktop/tmp.rb:43:in `new'
from C:/Users/gkistner.NVIDIA.COM/Desktop/tmp.rb:43:in `<main>'
Ah-HA! The problem is that we call show_current_description
as part of our initialize
method, before we've had a chance to add rooms to the dungeon.
Here are some ways we could fix this:
Change the Dungeon constructor to accept a list of rooms as part of initialization, so we can do this all at once. Here are three different ways one could do it:
# Method number one, accept an array of rooms class Dungeon def initialize(name,start,rooms=[]) @player = ... @rooms = rooms yield(self) if block_given? show_current_description end end d = Dungeon.new( "Bob", :small_cave, [ Dungeon::Room.new(...), Dungeon::Room.new(...) ] ) # Method 2: Allow an initializer block class Dungeon def initialize(name,start) @player = ... @rooms = [] yield(self) if block_given? show_current_description end end d = Dungeon.new( "Bob", :small_cave ) do |dungeon| dungeon.add_room(...) dungeon.add_room(...) end # Method 3 (experts only!): Initialize block runs in scope class Dungeon def initialize(name,start,&initializer) @player = ... @rooms = [] instance_eval(&initializer) if initializer show_current_description end end d = Dungeon.new( "Bob", :small_cave ) do add_room(...) add_room(...) end
Don't show the current description as part of the initializer. Just remove that line, and when running show the description when construction is complete:
d = Dungeon.new(...) d.add_room(...) d.add_room(...) d.show_current_description
Make your
show_current_description
method more robust, so that it can handle thenil
case:def show_current_description @rm = find_room_in_dungeon(@player.location) puts @rm.full_description if @rm end
You could choose to do 1 or 2, but I'd suggest also doing 3.
The 4th line from the bottom, @rooms :largecave})
, is a syntax error. The real code surely looks different.
Anyway, "undefined method full_description for nil:NilClass" means that @rm, returned by find_room_in_dungeon
, is nil.
Did you remove the puts in the @rooms.detect {...} ?
Remove it, and it should work.
精彩评论