Why is my variable not defined and this loop ends?

I ran into this problem. I have a job, so I ask, why is this not working? Why is the variable not determined for the first time through the loop, and then exits the loop?

Assuming this is an area that I'm sure is, why is foo determined after breaking the loop? Am I just seeing an irb artifact here?

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> 
+7
ruby
source share
2 answers

Method defined? does not check if a value is defined for a variable, but instead if that variable is defined in the area in which you are testing it.

The code you wrote here is basically meaningless:

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

The variable foo never "defined" outside the context of the while block, so it will always rotate, expecting this to happen. You end up defining it again in the context of the while , but the check is defined? Don't check it out. It is determined, but not where you expected.

If you add a little code to find out what happens, you get the following:

 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" 

The generic template you are going to use in Ruby's idiom code is more like this

 foo = nil while (!foo) foo = 1 end 

As a note, it is very unusual to see what is defined? used in Ruby applications because variables are used or not used. This is in situations where you have some automatic code generation and blocking lock, that you want to check local variables before using them. The most common case of this rare behavior is partial Rails splitting, in which you may have passed parameters with the :local argument. They appear as local variables, if defined, but may not exist, so you need to check to make sure.

+6
source share

Regardless of whether a variable is defined, it depends not only on the scope, but also on its location in the ruby ​​script. That is, a variable is defined only if it was previously defined in parsing, and not in execution.

Here is an example:

 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 

Output:

 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 

Since foo was not previously defined in the script in the first line of the loop, at this point it is undefined and remains undefined, even if it is assigned the same line returns to a later point at run time.

This is why foo in the while condition of the question is always undefined:

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

and will be cyclically forever. In contrast, this loop runs only once:

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

since foo is assigned earlier in parsing.


Edit:

Only loops that require a block appear to be isolated local variables from life outside of it. For example. loop , upto , each , inject , map , times , etc. To do this, they all require the use of the do and end or curly braces that bound the block. On the contrary, while , until and for do not, and therefore the variables defined inside them continue to live outside of them. This is shown here:

 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}" 

Output:

 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 
+3
source share

All Articles