8

如果我有:

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

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

更新:感谢您对此的帮助。我的部分困惑源于从 Python 进入 Ruby,它没有块作用域(我认为)。

4

6 回答 6

10

I don't know what your expectations are based on. If you think what I think you think, it should be 1 2 2 3. You can achieve that by declaring variable i outside of the block.

i = nil

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

Then the block closes over that variable (closure) and uses it. Without closure, i is local to the block and is new every time.

于 2013-05-15T17:51:55.413 回答
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

于 2013-05-15T17:53:07.933 回答
3

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

如果您不希望将变量理解为块的本地变量,则它需要预先存在于块之外。即使只是i在前一行中设置为零也可以做到这一点。

(但您的期望1 2 3 4仍然无法满足......!)

于 2013-05-15T17:50:27.387 回答
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"

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

于 2013-05-15T20:17:19.247 回答
1

简单的答案是您i在每次迭代时重新实例化变量并将其重置为 1。

于 2013-05-15T20:46:48.550 回答
0

Ruby 循环类似于x.times 创建本地范围,因此在此循环中引入的变量是本地的,并且将在到达块末尾后被销毁。这就是为什么您没有得到预期结果的原因。在 ruby​​ 中,如果您想要没有本地范围的循环,您可以使用fororwhile不创建本地范围的循环,但您的本地变量i在循环后仍可访问。

for j in (1..2)
  i ||= 1
  print "#{i} "
  i += 1
  print "#{i} "
end

打印1 2 2 3为您的预期结果。现在,如果我们在循环老化之上运行,它将给出结果3 4 4 5。另一方面,对于额外的信息whilefor工作相同(范围更少和打印),并且工作相同(创建本地范围和打印)1 2 2 3eachx.upto(y)x.times1 2 1 2

于 2015-09-10T08:44:24.673 回答