为什么我的变量没有定义,并结束这个循环?

时间:2014-07-02 21:11:57

标签: ruby

我遇到了这个问题。我有一个工作,所以我特别问为什么这不起作用?为什么变量在第一次循环中没有定义,然后退出循环?

假设它是我确信它的范围,那么为什么在打破循环后定义了foo?我刚看到irb的神器吗?

voin0017:[/home/acowell/src/local/goldrhel] ruby -v
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux]
voin0017:[/home/acowell/src/local/goldrhel] irb
irb(main):001:0> while defined?(foo).nil? ; foo = 1 ;  end
^CIRB::Abort: abort then interrupt!
        from (irb):1:in `call'
        from (irb):1
        from /opt/chef/embedded/bin/irb:12:in `<main>'
irb(main):002:0> p foo
1
=> 1
irb(main):003:0>

2 个答案:

答案 0 :(得分:6)

defined?方法不会测试是否为变量定义了,而是在该范围内定义了该变量,并对其进行了测试。

您在此处撰写的代码基本上是荒谬的:

while (defined?(foo).nil?)
  foo = 1
end

永远不会定义foo变量&#34;在while块的上下文之外,所以它永远旋转等待它发生。您最终在while循环的上下文中重复定义它,但defined?检查不会测试它。它正在被定义,而不是你预期的那样。

如果您添加一些代码来查看正在发生的事情,请获取此信息:

puts defined?(foo).inspect
# => nil

while (true)
  foo = 1

  puts defined?(foo).inspect
  # => "local-variable"

  break
end

# Once the while completes, the variable is defined outside that scope.
puts defined?(foo).inspect
# => "local-variable"

您在惯用Ruby代码中使用的一般模式看起来更像是

foo = nil

while (!foo)
  foo = 1
end

作为一个注释,在Ruby应用程序中使用defined?非常不寻常,因为变量已被使用或未被使用。在您进行自动代码生成和块重新绑定的情况下,您希望在使用它们之前测试局部变量。这种罕见行为的最常见情况是在Rails部分中,可能通过:local参数传递了选项。如果已定义,它们将显示为局部变量,但可能不存在,因此您需要进行测试以确定。

答案 1 :(得分:3)

是否定义变量不仅取决于范围,还取决于其在ruby脚本中的位置。也就是说,只有先前在 parse 中定义了变量,而不是执行,才定义变量。

以下是一个例子:

begin
    puts "Is foo defined? #{defined?(foo).inspect}" # foo is never defined here
    foo ||= 1
    puts "but now foo is #{foo}" # foo is always defined here
    foo += 1
end while foo <= 3

输出:

Is foo defined? nil
but now foo is 1
Is foo defined? nil
but now foo is 2
Is foo defined? nil
but now foo is 3

因为先前在循环的第一行的脚本中没有定义foo,所以它在那时未定义,并且保持未定义,即使它被分配给并且同一行被返回到at执行期间的后一点。

这就是问题的foo条件中的while始终未定义的原因:

while defined?(foo).nil? # foo is always undefined
    foo = 1
end

并将永远循环。相反,这个循环只执行一次:

begin
    foo = 1
end while defined?(foo).nil? # foo is defined

因为foo先前已在解析中分配。


修改

只有需要块的循环似乎将其局部变量与生活在其外部隔离开来。例如。 loopuptoeachinjectmaptimes等。这些都需要使用关键字doend,或花括号,分隔块。相比之下,whileuntilfor没有,因此在其中定义的变量继续存在于其之外。这在这里得到证明:

while true
    foo_while = 1
    break
end
puts "foo_while: #{defined?(foo_while).inspect}"

until false
    foo_until = 1
    break
end
puts "foo_until: #{defined?(foo_until).inspect}"

for i in 0..2
    foo_for = 1
    break
end
puts "foo_for: #{defined?(foo_for).inspect}"

loop do
    foo_loop = 1
    break
end
puts "foo_loop: #{defined?(foo_loop).inspect}"

1.upto(2) do |i|
    foo_upto = 1
    break
end
puts "foo_upto: #{defined?(foo_upto).inspect}"

[1,2,3].each do |i|
    foo_each = 1
    break
end
puts "foo_each: #{defined?(foo_each).inspect}"

[1,2,3].inject do |i,j|
    foo_inject = 1
    break
end
puts "foo_inject: #{defined?(foo_inject).inspect}"

[1,2,3].map do |i|
    foo_map = 1
    break
end
puts "foo_map: #{defined?(foo_map).inspect}"

3.times do
    foo_times = 1
    break
end
puts "foo_times: #{defined?(foo_times).inspect}"

输出:

foo_while: "local-variable"
foo_until: "local-variable"
foo_for: "local-variable"
foo_loop: nil
foo_upto: nil
foo_each: nil
foo_inject: nil
foo_map: nil
foo_times: nil