21

我有一个名为 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

4 回答 4

22

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

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

请注意,如果您确实希望您的test方法能够访问定义上下文中定义的局部变量,请使用define_method(或在您的情况下为define_singleton_method方法),请参见此处:

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

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

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

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

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

于 2012-05-28T10:11:58.447 回答
14

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 为零?

以下内容可能会更清楚地说明这一点:

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

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

希望这有点道理。

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

于 2012-05-28T04:45:24.820 回答
2

在 ruby​​ 中,没有符号的名称始终具有“本地”范围。Ruby 使用词法作用域,因此在方法内部,名称始终仅指该方法。

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

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

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

更有可能的是,您将在类中封装您正在执行的任何操作并使用实例变量。

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

于 2012-05-28T04:49:39.960 回答
0

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

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

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

于 2012-05-28T12:04:00.103 回答