开发者

getting for nil:NilClass (NoMethodError) in this code [closed]

开发者 https://www.devze.com 2023-02-21 04:23 出处:网络
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time,or an extraordinarily narrow situation that is not generally applic
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center. Closed 11 years ago.

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:

  1. 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
    
  2. 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
    
  3. Make your show_current_description method more robust, so that it can handle the nil 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消