【问题标题】:Scope of a local variable in a block块中局部变量的范围
【发布时间】:2013-05-10 10:03:56
【问题描述】:

如果我有:

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

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

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

【问题讨论】:

  • 这是为了教学目的吗?因为这种代码完全不习惯......

标签: ruby block variable-assignment


【解决方案1】:

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

i = nil

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

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

【讨论】:

    【解决方案2】:

    看下面的代码:

    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" 证明。

    现在iblock 之外创建,并查看输出(没有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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

      • 实际上,即使只是 if false then i = nil end 就足够了,但确切地说 为什么 在这种情况下可能难以解释。
      【解决方案4】:

      您可以从中获得一些乐趣。比如说你想访问块内的范围。

      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"
      

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          x.times 这样的 Ruby 循环创建本地范围,因此在此循环中引入的变量是本地的,并且将在到达块末尾后被销毁。这就是为什么您没有得到预期结果的原因。 在 ruby​​ 中,如果您想要没有本地范围的循环,您可以使用 forwhile 循环,它不会创建本地范围,但您的局部变量 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 3)另一方面 eachx.upto(y)x.times 工作相同(创建本地范围并打印 1 2 1 2

          【讨论】:

            猜你喜欢
            • 2014-07-14
            • 2022-01-12
            • 2014-03-02
            • 1970-01-01
            • 1970-01-01
            • 2013-10-14
            • 1970-01-01
            • 1970-01-01
            • 2010-11-26
            相关资源
            最近更新 更多