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

use*_*222 0 ruby

我正在开始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
Run Code Online (Sandbox Code Playgroud)

Phr*_*ogz 6

您发布的错误意味着此行......

@rm.full_description
Run Code Online (Sandbox Code Playgroud)

...正试图调用full_description没有这种方法的@rm对象,因为对象是nil.从那以后我们可以推断出前一行......

@rm=find_room_in_dungeon(@player.location)
Run Code Online (Sandbox Code Playgroud)

...设置@rmnil作为结果find_room_in_dungeon的方法.如果我们看看那种方法......

@rooms.detect{|room| room.reference == reference; puts room.full_description}
Run Code Online (Sandbox Code Playgroud)

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

@rooms.detect{|room| room.reference == reference }
Run Code Online (Sandbox Code Playgroud)

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

首先,让我们验证我们关于这种方法的前提条件的假设.在更改行之前,我们添加p @rooms, referencefind_room_in_dungeon方法中.现在运行代码,我们看到这个输出:

[]
:small_cave
Run Code Online (Sandbox Code Playgroud)

啊,哈!这就是问题所在.我们正在寻找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>'
Run Code Online (Sandbox Code Playgroud)

啊,哈!问题是,在我们有机会为地牢添加房间之前,我们称之为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
    
    Run Code Online (Sandbox Code Playgroud)
  2. 不要将当前描述显示为初始化程序的一部分.只需删除该行,并在运行时显示构造完成时的描述:

    d = Dungeon.new(...)
    d.add_room(...)
    d.add_room(...)
    d.show_current_description
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使您的show_current_description方法更健壮,以便它可以处理nil案例:

    def show_current_description
      @rm = find_room_in_dungeon(@player.location)
      puts @rm.full_description if @rm
    end
    
    Run Code Online (Sandbox Code Playgroud)

你可以选择做1或2,但我建议做3.