2011-06-12 146 views
0

我很困惑块内变量的范围。这工作:范围令人困惑

f = 'new' 
[1,2,3].each do |n| puts f * n end 
#=> new newnew newnewnew 

但这并不:

[1,2,3].each do |n| 
    a ||=[] 
    a << n 
end 
a 
#=>a does not exsit! 

这是为什么?请为我提供一些关于这个话题的资源。

+1

@sawa感谢您再次纠正我的问题 – mko 2011-06-14 09:19:05

回答

7

什么让人困惑?

在第一片断f被创建并随后each块被执行,它可以看到外面本身的东西(称为封闭的范围)。所以它可以看到f

在第二个片段中,您在该块内部创建了a,因此其范围就是该块。在块之外,a不存在。

当你引用一个名字时(例如a),ruby将从当前作用域向外移动,查看所有封闭作用域的名称。如果它在其中一个封闭范围内找到它,它将使用与该名称关联的值。如果不是,则返回大多数本地范围并在那里创建名称。随后的名称查找将产生与该名称关联的值。

块结束时,该范围内的名称将丢失(不会丢失,只是名称;当垃圾回收器发现没有更多名称(或任何内容)引用时,值会丢失该值,并且gc收集该值以重用其内存)。


如果可视化是你的事情,我觉得把范围想象成楼梯是有帮助的,在程序开始时,你站在最前面的步骤。每输入一个块,你就下一步。您可以在当前步骤中看到所有内容,并且可以看到所有步骤都在您正在使用的步骤之上,但下面的步骤中没有任何内容。当你引用一个变量名时,你可以看看你正在寻找它的步骤。当你看到它时,你使用该值。如果你没有看到它,你会看到你正在上面的下一个步骤。如果你看到它,你使用该值。你一直这样做,直到你看到了最高的一步,但没有看到那个名字。如果发生这种情况,您可以在您所站的步骤上创建名称(如果您正在查看它的作业,请给它一个值)。下一次你寻找那个名字时,你会在你站在的那一步看到它,并在那里使用它。

当一个模块结束时,您将踏上一个台阶。由于您在下面的步骤中看不到任何名称,因此您之前步骤中的所有名称都会丢失。

如果这可以帮助你,那么想一想。如果没有,不要。

其实你在第二个步骤,因为你在全球范围内不是,但是从全球范围内使用的名称,你必须在名称的开头使用$。因此,在楼梯示例中,如果您要查找的名称在开头处具有$,则可直接查看最上面的步骤。如果没有,你看起来不那么远。然而,这是错误的,因为程序中的所有楼梯都会共享相同的顶级步骤,这是很奇怪的想法。

+1

+1。这取决于范围'a'是***第一***使用。 – 2011-06-12 17:32:42

+0

如果我使用比喻,我会提到一辆带烟熏玻璃的汽车;你可以从内部看到外部,但不是相反。 – sawa 2011-06-12 17:57:15

+0

@sawa是的,但你有嵌套的范围,这将像汽车内的汽车。然后事情开始变得混乱。 – 2011-06-12 17:58:00

1

很简单,一个块中定义的变量是不可见的外(如果发生这种情况,我们会说变量已经泄露,并作为顾名思义,这将是坏的):

>> lambda { x = 1 }.call 
=> 1 
>> x 
NameError: undefined local variable or method `x' for main:Object 
2

地图的效果要好得多:

a = [1,2,3].map do |n| 
    n 
end 

不需要声明a外块。

+0

你是对的!但我只想举例说明我不能在范围之外使用的变量 – mko 2011-06-14 09:45:28