nil的未定义方法`%':NilClass

时间:2014-08-27 23:40:57

标签: ruby nomethoderror fixnum

对于以下代码块,我得到“NoMethodError: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

我知道'秒'是一个Fixnum因为我在没有#to_s的情况下尝试#puts秒时得到NoM​​ethod: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

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

2 个答案:

答案 0 :(得分:2)

我对此并不是100%肯定,但我认为这是一个优先问题。

由于运算符始终优先于方法,因此在评估%= 2 getter方法之前,正在评估seconds。我认为这就是导致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

答案 1 :(得分:2)

它是在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