【问题标题】:undefined method `%' for nil:NilClassnil:NilClass 的未定义方法“%”
【发布时间】:2014-08-27 23:40:56
【问题描述】:

我收到以下代码块的“NoMethodError: undefined method `%' for nil:NilClass”:

class Timer
    attr_accessor :seconds

    def initialize
        @seconds = 0
    end

    def time_string

        if seconds < 10
            return "00:00:0" + seconds.to_s
        elsif seconds < 60
            return "00:00:" + seconds.to_s
        elsif seconds < 540
            minutes = seconds / 60
            seconds %= 60
            #seconds = seconds - (minutes * 60)
            return "00:0" + minutes.to_s + ":0" + seconds.to_s
        end
    end

    def timer
        @timer          
    end
end

我知道 'seconds' 是一个 Fixnum,因为当我尝试在没有 #to_s 的情况下 #puts seconds 时出现 NoMethod: Fixnum 错误。此外,前一行中“秒”上的“/”操作也可以正常工作。那么为什么我会收到 NoMethod:nilclass 错误消息?

为什么我什至会收到一条错误消息? “%”不应该在“/”的任何地方都可以工作吗?

以下代码有效:

        if seconds < 10
            return "00:00:0" + seconds.to_s
        elsif seconds < 60
            return "00:00:" + seconds.to_s
        elsif seconds < 540
            minutes = @seconds / 60
            seconds = @seconds % 60
            return "00:0" + minutes.to_s + ":0" + seconds.to_s
        end

它与实例变量有关,我不了解实例变量。很想知道零是如何进入那里的。

【问题讨论】:

  • 以上应该可以正常工作,请参阅:rubyfiddle.com/riddles/dbaa9
  • 谢谢。真让人抓狂。
  • 更新显示整个类,而不仅仅是方法。注释掉的行会产生相同的错误。就像所有的操作员突然停止工作一样。
  • 代码有效,见这里:rubyfiddle.com/riddles/5e554。您的问题在我认为的其他地方。
  • 我看到了,并用 ruby​​fiddle 代码代替了我自己的代码。得到了同样的结果。不知道问题可能出在哪里,这是一个教程,所以实际上只有这个和 rake 文件。

标签: ruby nomethoderror fixnum


【解决方案1】:

这是在 self 上调用的方法、局部变量以及 Ruby 在这些事物之间缺乏语法区别之间的交互。

如果你把这行改成这样:

         self.seconds %= 60

然后它工作正常。

问题在于,当 Ruby 看到对非限定名称的赋值时,它会使用该名称创建一个局部变量,而不是寻找访问器。

这是一个简单的演示:

irb(main):001:0> def foo=(n)
irb(main):002:1>  puts "Calling foo!"
irb(main):003:1>  @foo=n
irb(main):004:1> end    #=> nil
irb(main):005:0> foo=1    #=> 1
irb(main):006:0> @foo    #=> nil
irb(main):007:0> self.foo=2
Calling foo!
=> 2
irb(main):008:0> @foo    #=> 2

【讨论】:

  • 谢谢。这是一个我很难理解的概念。
  • 这很简单。当 Ruby 看到像 foo.bar = baz 这样的合格赋值时,它会在 foo 引用的对象上查找访问器方法 bar=。但是当它看到像bar=baz 这样的非限定赋值时,它不会寻找任何访问器方法,因为不存在任何对象。它只是创建一个局部变量。不合格的显式方法调用将被发送到self这一事实并不适用。
【解决方案2】:

我对此不是 100% 确定,但我相信这是一个优先问题。

由于运算符始终优先于方法,因此在评估 seconds getter 方法之前评估 %= 2。我相信这就是导致NoMethodError 的原因。

这也可以解释为什么使用@seconds 有效,因为您直接引用实例变量,而不是使用attr_accessor 在幕后创建的getter 方法。

作为旁注,我认为从类设计的角度来看,在这种情况下使用实例变量更有意义。

编辑:我上面说的不可能是正确的,因为像这样的方法有效:

class Book
  attr_accessor :title, :length

  def midpoint
    length / 2
  end
end

我认为实际上要简单得多。我假设%=+= 等其他赋值运算符的工作方式相同,因为编写seconds %= 60 与编写seconds = seconds % 60 相同。

那么可能发生的事情是,由于您将某些内容分配给seconds,ruby 将其解释为一个名为seconds 的新局部变量。当%= 被“扩展”为seconds = seconds % 60 时,右侧的seconds 被解释为同一个局部变量,当前为nil。因此,NoMethodError.

【讨论】:

  • 编辑是正确的答案。如果您将seconds 替换为self.seconds(显式调用访问器)或@seconds(绕过访问器并直接访问实例变量),代码将按预期工作。
猜你喜欢
  • 2011-07-27
  • 2013-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多