获取nil:此代码中的NilClass(NoMethodError)

时间:2011-04-01 20:18:47

标签: ruby

我正在做一本红宝石书,这就是我的代码,它给出了以下错误:

in 'show_current_description': undefined method 'full_description' for nil:NilClass (NoMethodError)

代码如下。任何帮助表示赞赏。谢谢!

class Dungeon
  attr_accessor :player

  def initialize(player_name,start_location)
    @player=Player.new(player_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

3 个答案:

答案 0 :(得分:6)

您发布的错误表示此行...

@rm.full_description

...正在尝试在没有此类方法的对象上调用full_description,因为@rm对象是nil。由此我们可以推断出前一行......

@rm=find_room_in_dungeon(@player.location)

...将@rm设置为nil作为find_room_in_dungeon方法的结果。如果我们看一下这种方法......

@rooms.detect{|room| room.reference == reference; puts room.full_description}

......我们看到一个问题。 detect方法使用块的返回值来确定是否应该使用房间。但是,puts方法(块中的最后一个方法,因此块的返回值)始终返回nil。因此,@rooms.detect永远不会找到任何东西。因此,让我们通过完全删除puts来解决这个问题:

@rooms.detect{|room| room.reference == reference }

这应该有所帮助,但它没有解决问题。我们仍然从此方法获得nil。让我们看一下。

首先,让我们验证我们对这种方法的前提条件的假设。在更改行之前,我们会向p @rooms, reference方法添加find_room_in_dungeon。现在运行代码,我们看到了这个输出:

[]
:small_cave

阿公顷!这就是问题所在。我们正在寻找small_cave房间,但我们的房间清单是空的!我们知道我们在脚本的底部调用d.add_room ...为什么它不起作用?

让我们看看这段代码破坏时的位置。以下是错误消息的完整回溯:

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>'

阿-HA!问题是,在我们有机会为地牢添加房间之前,我们会将show_current_description作为initialize方法的一部分。

以下是我们解决此问题的一些方法:

  1. 更改Dungeon构造函数以接受房间列表作为初始化的一部分,因此我们可以一次完成所有操作。以下是三种不同的方式:

    # 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. 不要将当前描述显示为初始化程序的一部分。只需删除该行,并在运行时在构造完成时显示描述:

    d = Dungeon.new(...)
    d.add_room(...)
    d.add_room(...)
    d.show_current_description
    
  3. 让您的show_current_description方法更加健壮,以便它可以处理nil案例:

    def show_current_description
      @rm = find_room_in_dungeon(@player.location)
      puts @rm.full_description if @rm
    end
    
  4. 你可以选择做1或2,但我建议也做3。

答案 1 :(得分:1)

底部的第4行@rooms :largecave})是语法错误。真正的代码肯定看起来不同。

无论如何,“nil:NilClass的未定义方法full_description”表示由find_room_in_dungeon返回的@rm为零。

答案 2 :(得分:0)

您是否删除了@ rooms.detect {...}中的看跌期权?

删除它,它应该可以工作。