我有一个名为test.rb的Ruby文件
ff="ff"
def test
puts ff
end
Run Code Online (Sandbox Code Playgroud)
我执行它,得到错误:
test.rb:3:in `test': undefined local variable or method `ff' for main:Object (NameError)
这是什么原因?有没有文件可以解释它?
hor*_*guy 17
原因ff
是test
方法定义中无法访问的原因很简单,方法(使用def
关键字创建)创建了一个新范围.与分别使用class
和module
关键字定义类和模块相同.
main
在这种情况下,(顶级对象)的作用几乎完全与范围问题无关.
请注意,如果您希望您的test
方法可以访问定义上下文中定义的本地,那么请使用define_method
(或者在您的情况下,define_singleton_method
方法),请参阅此处:
ff = "hi"
define_singleton_method("test") { ff }
test #=> "hi"
Run Code Online (Sandbox Code Playgroud)
与def
关键字不同define_method
,方法系列不会创建新范围,而是关闭当前范围,捕获任何局部变量.
@ff
在@soup给出的下一个例子中使用的原因并不是main
在某种程度上是一个"特殊情况",只是在顶层定义的ivar是一个ivar,main
所以可以访问调用的方法main
.
然而,该test
方法的关系是main
什么?这不是一个方法就只是main
本身-它实际上是在定义的私有实例方法 Object
类.这意味着该test
方法几乎可用于ruby程序中的每个对象(作为私有方法).top-level(main
)中定义的所有方法实际上都定义为Object
类上的私有实例方法.
有关Ruby顶级的更多信息,请参阅此文章:http://banbanfiend.wordpress.com/2010/11/23/what-is-the-ruby-top-level/
Sou*_*oup 11
Ruby范围既简单又复杂.
首先,你必须记住,一切都是一个对象,一切都有范围.
直接回答你的问题main
是一个对象,当你写这个对象时,def x...
你正在定义一个方法main
观察pyr/irb:
# note the error describes
[1] pry(main)> main 'main:Object'
NameError: undefined local variable or method 'main' for main:Object
from (pry):1:in '<main>'
# self is the current object, which is main
[2] pry(main)> self
=> main
# note main is an object of type Object
[3] pry(main)> self.class
=> Object
# main has methods
[4] pry(main)> self.methods
=> [:to_s, :public, etc etc]
Run Code Online (Sandbox Code Playgroud)
所以当你写作
ff = "ff"
def test
puts ff
end
Run Code Online (Sandbox Code Playgroud)
你真正在做的是
class main
ff = "ff"
def test
puts ff
end
end
Run Code Online (Sandbox Code Playgroud)
哪个不起作用,因为ff
超出了范围.要修复此问题,您必须ff
转换为实例变量,因此请在其前面添加名称@
.以下将有效:
@ff = "ff"
def test
puts @ff
end
test
Run Code Online (Sandbox Code Playgroud)
请注意,这似乎是main
常规类的特殊情况,请参阅下文.
如果我们有自己的测试类:
class Test1
ff = "ff"
def test
puts ff
end
end
Test1.new.test # undefined variable/method 'ff' error
Run Code Online (Sandbox Code Playgroud)
这会失败,因为ff
没有按预期在正确的范围内定义.当解析器执行我们的类声明时,它被限定为范围.
所以让我们尝试上面的修复:
class Test1Fixed
@ff = "ff"
def test
puts @ff
end
end
Test1Fixed.new.test # results in blank line
Run Code Online (Sandbox Code Playgroud)
那真是怪了.
让我们添加这个方法:
def ff?
puts "ff is: #{@ff.nil? ? "nil" : "not nill"}"
end
Test1Fixed.new.ff? # => ff is: nil
Run Code Online (Sandbox Code Playgroud)
为什么@ff为零?
以下内容可能会更清楚:
class Test2
puts "Where am i?"
@instance = "instance"
def initialize
puts "Initalize"
end
puts "pants on"
def test
puts "This will NOT output 'instance': #{@instance}"
end
def self.class_test
puts "This WILL output 'instance': #{@instance}"
end
end
puts "Creating new Test2 Object"
t = Test2.new
puts "Calling test on Test2 Object"
t.test
puts "Calling class_test on Test2 Class object"
Test2.class_test
Run Code Online (Sandbox Code Playgroud)
运行它我们得到以下输出:
$ ruby scope.rb
Where am i?
pants on
Creating new Test2 Object
Initalize
Calling test on Test2 Object
This will NOT output 'instance':
Calling class_test on Test2 Class object
This WILL output 'instance': instance
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,interperter按顺序运行我们的类声明,就像其他地方一样,并输出我们的puts
语句.当我们开始调用方法时,这会变得更有趣.写作@instance = ...
与写作相同self.instance = ...
,那么你能猜出我们定义实例的位置吗?是的,在Test2类对象(不是Test2对象)上.
这就是为什么当我们调用时test
,没有任何东西被输出,因为在内部test
,self
指的是instanciated Test2
对象,而不是Test2 class object
(这是我们将@instance设置为任何东西!).这就是你在里面设置实例变量的原因initialize
,self
它将指向实际的对象.
您可以看到我们在定义类方法时self.class_test
,然后在Test2类对象(Test2.class_test
)上调用该方法,我们得到预期的输出,因为在该范围内,@instance
已定义.
希望这有一定道理.
该http://rubykoans.com/范围部分可能有助于巩固一些这方面的知识.