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

时间:2012-05-28 03:53:54

标签: 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)

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

4 个答案:

答案 0 :(得分:13)

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

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

请注意,如果您希望test方法可以访问定义上下文中定义的本地,那么请使用define_method(或者在您的情况下,使用define_singleton_method方法) ,见这里:

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

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

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

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

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

答案 1 :(得分:10)

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]

所以当你写

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 nil?

以下内容可能会更清楚:

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时,没有任何内容被输出,因为在test内,self指的是实例Test2对象,而不是Test2 class object (这是我们将@instance设置为任何东西的地方!)。这就是您在initialize中设置实例变量的原因,其中self将指向实际对象。

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

希望这是有道理的。

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

答案 2 :(得分:2)

在红宝石中,没有印记的名字总是有&#34;本地&#34;范围。 Ruby使用词法作用域,因此在方法中,名称始终只引用该方法。

其他范围是全局,实例和类。这是一个很好的文件:  http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Operators#Scope

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

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

更有可能的是,您将封装您在课堂上所做的任何事情以及使用实例变量。

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

答案 3 :(得分:0)

  

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

因为它是本地变量。局部变量是它们定义的范围的局部变量。毕竟,这就是它们被称为“局部变量”的原因。

在这种情况下,您已在脚本范围中定义了一个局部变量,因此它在脚本范围内可见,并且无处其他