2015-11-24 123 views
5

考虑以下两段ruby代码片段。Ruby中的变量范围

puts "One" 
if false 
    d = 1 
end 
puts "Two" 
puts d 
puts "Three" 

这将打印以下

One 
Two 

Three 

现在,请考虑以下

[].each do |i| 
    flag = false 
end 
puts "Two" 
puts flag 
puts "Three" 

这给出了以下

Two 
'<main>': undefined local variable or method 'flag' for main:Object (NameError) 

为什么在第一种情况下空白打印和第二ca是否引发错误?

感谢

+0

的[我不明白红宝石局部范围]可能的复制(http://stackoverflow.com/questions/4154864/ i-dont-understand-ruby-local-scope) –

+1

块创建一个新的作用域,因此在该作用域之外的变量未被定义。 '如果'另一方面 - 不。当解释器看到一行代码将赋予一个变量值时,它会确保它首先被定义并用'nil'初始化它。这也是为什么'foo = bar'会给你一个错误,而'baz = baz'不会。 – ndn

+2

在第二种情况下,'flag'变量是该块的范围,因此在外面不可见(http://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/localvars.html)。在第一种情况下,即使代码块没有执行,该变量也会被定义 - 这一部分我不确定 - 可能是Ruby解释器将其标记为已定义 –

回答

5

区别在于if块实际上并不像其他语言(如Java)中的单独范围。在if块中声明的变量与周围环境具有相同的范围。现在,在你的情况下,那个if块实际上不会被执行,所以你通常会认为d是未定义的(导致你在第二个例子中得到同样的错误)。但是ruby有点“傻瓜”,因为解释器在它看到它的时候会设置一个带有该标签的变量,而不管它是否被实际执行,因为它基本上不知道该分支是否确实会执行。这在"The Ruby Programming Language" David Flanagan着和松本行弘解释(不能复制粘贴文本,添加截图代替): enter image description here

.each循环的情况下,do...end你写的其实是一个block ,它确实有它自己的本地范围。换句话说,块内声明的变量仅在该块的本地。

但是,块“继承”了它们声明的环境的范围,因此您可以做的是在.each迭代块之外声明flag,然后该块将能够访问它并设置其值。请注意,在你给出的例子中,这不会发生,因为你试图迭代一个空数组,但至少你不会再收到错误。

一些额外阅读:

+1

非常感谢您的详细解答。它非常有帮助:) – Jim

+0

@Jim我的荣幸,很高兴它有所帮助。 –

2

在Ruby中,当你做一个赋值给一个变量(在你的情况下,它d)在if语句的假分支的任何地方,它宣称,除非方法d=定义这个变量。基本上b = bla-bla-bla在False分支使这个:b = nil

当您在空数组上使用块时,什么都不会发生。并且如果数组不为空可变仍然是本地的块的当前迭代,除非它是外块范围定义,例如:

[1,2,3,4].each do |i| 
    a=i 
end 
puts a 

NameError: undefined local variable or method `a' for main:Object

a=1 
[1,2,3,4].each do |i| 
    a=i 
end 
puts a 

4

您也有一个选择使用a作为块内的本地的,如果它已被前面所定义:

a=1 
[1,2,3,4].each do |i; a| 
    a=i 
end 
puts a 

1

+0

感谢您的解释以及:) – Jim