范围令人困惑

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

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

但这不是:

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

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

什么令人困惑?

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

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

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

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


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

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

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

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

地图效果更好:

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

无需声明块外。

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

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