范围令人困惑

时间:2011-06-12 17:29:01

标签: ruby

我对块内变量的范围感到困惑。这有效:

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!

这是为什么?请为我提供一些资源。

3 个答案:

答案 0 :(得分:7)

有什么令人困惑的?

在第一个代码段f中创建,然后执行each块,它可以看到外部的东西(称为封闭范围)。所以它可以看到f

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

当您引用名称(例如a)时,ruby将从当前范围向外,查看名称的所有封闭范围。如果它在其中一个封闭范围内找到它,则使用与该名称关联的值。如果没有,它将返回最本地范围并在那里创建名称。后续的名称查找将产生与该名称相关的值。

当一个块结束时,该范围内的名称将丢失(不会丢失,只会丢失名称;当垃圾收集器看到不再有名称时,值会丢失(或者任何事情)引用该值,并且gc收集值以重用其内存)。

<小时/> 如果可视化是您的事情,我发现将范围视为一个阶梯是有帮助的,并且在程序开始时,您站在最顶层 1 。每次输入一个块时,您都会退一步。您可以看到当前步骤中的所有内容,以及您所在位置上方的所有步骤,但以下步骤中没有任何内容。当您引用变量名称时,可以查看您正在查找的步骤。当你看到它时,你使用那个值。如果你没有看到它,你会看到你正在进行的下一步。如果你看到它,你就使用那个值。你一遍又一遍地这样做,直到你看到最顶端的一步,但没有看到这个名字。如果发生这种情况,您可以在您所站立的步骤上创建名称(如果您正在查找作业,则为其指定一个值)。下次您查找该名称时,您会在您站立的步骤中看到它,并在那里使用它。

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

如果这对你有所帮助,那就这么想吧。如果没有,请不要。

1 实际上你是第二步,因为你不在全球范围内,但要使用全局范围的名称,你必须在开头使用$名称。所以在楼梯示例中,如果您要查找的名称在开头有一个$,则直接查看顶部步骤。如果没有,你看起来不那么远。然而,这有点不对,因为程序中的所有楼梯都会分享相同的顶级步骤,这是一个奇怪的想法。

答案 1 :(得分:2)

地图效果更好:

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

无需在区块之外声明a

答案 2 :(得分:1)

很简单,在一个块内定义的变量在外面是不可见的(如果发生这种情况,我们会说变量已经泄漏了,正如这个词暗示的那样,这会很糟糕):

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