对于以下代码块,我得到“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秒时得到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
它与实例变量有关,而我不理解实例变量。我很想知道nil是如何进入那里的。
答案 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