块中局部变量的范围

时间:2013-05-15 17:48:29

标签: ruby block variable-assignment

如果我有:

2.times do
  i ||= 1
  print "#{i} "
  i += 1
  print "#{i} "
end

我得到1 2 1 2,而我期待1 2 2 3。为什么i在循环重新开始时会丢失它的赋值?如果赋值发生在循环之外,它的行为与预期一致,所以我猜它与范围有关,但我没有意识到循环有自己的范围。有人可以澄清吗?

更新:感谢您的帮助。我的一些困惑源于从Python进入Ruby,它没有块范围(我认为)。

5 个答案:

答案 0 :(得分:10)

我不知道你的期望是基于什么的。如果你认为我认为你的想法,它应该是1 2 2 3。您可以通过在块外声明变量i来实现此目的。

i = nil

2.times do
  i ||= 1
  print "#{i} "
  i += 1
  print "#{i} "
end

然后该块关闭该变量(闭包)并使用它。没有闭包,i是块的本地,每次都是新的。

答案 1 :(得分:9)

请看下面的代码:

2.times do
  p defined? i
  i ||= 1
  p defined? i
  p "#{i} "
  i += 1
  p "#{i} "
end

输出:

nil 
"local-variable"
"1 "
"2 "
nil
"local-variable"
"1 "
"2 "

这意味着在每次迭代中都会创建一个新范围,i仅为该范围所知;这由nil"local-variable"证实。

现在i已在block之外创建,并看到输出(没有nil):

i = nil
2.times do
  p defined? i
  i ||= 1
  p defined? i
  p "#{i} "
  i += 1
  p "#{i} "
end

输出:

"local-variable"
"local-variable"
"1 "
"2 "
"local-variable"
"local-variable"
"2 "
"3 "

详细了解||=外观What Ruby’s ||= (Double Pipe / Or Equals) Really Does

答案 2 :(得分:3)

不是具有范围的“循环”。这是块。是的,块是本地范围。

如果您不希望变量被理解为块的本地变量,则需要事先在外部块中存在。即使只是在前一行中将i设置为nil也可以这样做。

(但你对1 2 3 4的期望仍然不会得到满足......!)

答案 3 :(得分:2)

你可以玩得开心。比如说你想访问块内的范围。

block = -> do
  x = "Hello from inside a block"
  binding # return the binding
end

p defined? x              #=> nil
x = eval "x", block.call  #=> #<Binding:0x007fce799c7dc8>
p defined? x              #=> "local-variable"
p x                       #=> "Hello from inside a block"

这很重要,因为它允许开发人员基本上吹掉块的封装,应该谨慎使用。

答案 4 :(得分:1)

简单的答案是您在每次迭代时重新定义变量i并重置为值1。