为什么我不能在Ruby中的方法中访问局部变量?

我有一个名为test.rb的Ruby文件

ff="ff" def test puts ff end 

我执行它,得到错误:

test.rb:3:in `test': undefined local variable or method `ff' for main:Object (NameError)

这是什么原因? 有没有文件可以解释它?

test方法定义中ff无法访问的原因很简单,方法(使用def关键字创建)创建了一个新范围。 与分别使用classmodule关键字定义类和模块相同。

main (顶级对象)的角色几乎完全与这种情况下的范围问题无关。

请注意,如果您希望test方法可以访问定义上下文中定义的locals,那么请使用define_method (或者在您的情况下,使用define_singleton_method方法),请参阅此处:

 ff = "hi" define_singleton_method("test") { ff } test #=> "hi" 

def关键字不同, define_method方法系列不会创建新范围,而是关闭当前范围,捕获任何局部变量。

@ff给出的下一个例子中使用@ff的原因并不是main在某种程度上是一个“特殊情况”,只是在顶层定义的ivar是一个main的ivar,因此可以访问调用的方法main

然而, test方法与main的关系是什么? 它不仅仅是main本身的方法 – 它实际上是在Object类上定义的私有实例方法。 这意味着test方法几乎可用于ruby程序中的每个对象(作为私有方法)。 在顶级( main )定义的所有方法实际上都定义为Object类上的私有实例方法。

有关Ruby顶级的更多信息,请参阅此文章: http : //banbanfiend.wordpress.com/2010/11/23/what-is-the-ruby-top-level/

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 '
' # 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]

所以当你写作

 ff = "ff" def test puts ff end 

你真正在做的是

 class main ff = "ff" def test puts ff end end 

哪个不起作用,因为ff超出了范围。 要解决此问题,您必须将ff转换为实例变量,因此将其名称前缀为@ 。 以下将有效:

 @ff = "ff" def test puts @ff end test 

请注意,这似乎是main比常规类的特殊情况,请参阅下文。

如果我们有自己的测试类:

 class Test1 ff = "ff" def test puts ff end end Test1.new.test # undefined variable/method 'ff' error 

这会失败,因为ff未按预期在正确的范围内定义。 它取而代之的是解析器执行我们的类声明时的范围。

所以让我们尝试上面的修复:

 class Test1Fixed @ff = "ff" def test puts @ff end end Test1Fixed.new.test # results in blank line 

那真是怪了。

让我们添加这个方法:

 def ff? puts "ff is: #{@ff.nil? ? "nil" : "not nill"}" end Test1Fixed.new.ff? # => ff is: nil 

为什么@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 

运行这个我们得到以下输出:

 $ 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 

正如您所看到的,interperter按顺序运行我们的类声明,就像其他任何地方一样,并输出我们的puts语句。 当我们开始调用方法时,这会变得更有趣。 编写@instance = ...与编写self.instance = ...相同,那么你能猜出我们定义实例的位置吗? 是的,在Test2类对象(不是Test2对象)上。

这就是为什么当我们调用test ,没有任何东西被输出,因为在testself指的是instanciated Test2对象,而不是Test2 class object (我们将@instance设置为任何东西!)。 这就是你在initialize设置实例变量的原因,其中self将指向实际的对象。

您可以看到我们通过self.class_test定义类方法然后在Test2类对象( Test2.class_test )上调用该方法时,我们得到了预期的输出,因为在该范围内,定义了@instance

希望这有一定道理。

http://rubykoans.com/范围部分可能有助于巩固其中的一些知识。

在ruby中,没有sigil的名称总是具有“本地”范围。 Ruby使用词法作用域,因此在方法中,名称始终仅引用该方法。

其他范围是全局,实例和类。 这是一篇关于它们的好文档: http : //en.wikibooks.org/wiki/Ruby_Programming/Syntax/Operators#Scope

有几种方法可以解决您的问题; 我最喜欢的是传递被打印的东西作为参数:

 ff = "ff" def test(ff) puts ff end 

更有可能的是,你将封装你在类中所做的任何事情并使用实例变量。

你也可以使用一个全局变量($ ff),但这种疯狂就是这样。

为什么我不能在Ruby中的方法中访问局部变量?

因为它是一个局部变量 。 局部变量是它们定义的范围的局部变量。这就是为什么它们被称为“局部变量”,毕竟。

在这种情况下,您已在脚本范围中定义了一个局部变量,因此它在脚本范围中可见,而在其他任何地方都不可见。