As a rule of thumb, variables exist until you reach the "end" or
closing brace of the innermost block that they're still contained in.
In your case, you have this:
def do_something
3.times do |x|
number = 10
end
puts number
end
The local variable `number` is created with the value 10 and then
immediately discarded. This happens three times, since the block
executes three times.
The goal of the next statement is to print a local variable called
`number`. But there is no such local variable in this block. So Ruby
rightfully complains that you didn't define it. If instead you had
written this:
def do_something
number = 0 # create `number` in this lexical scope
3.times do |x|
number = 10
end
puts number
end
There's no "setter" here. Both of those are local variable assignments. In
the second example, the variable is created local to the block. After the
block ends, the variable ceases to exist.
If you want the variable to exist in the outher block, just create it
there:
class ABC
def do_something
number = 0
3.times do |x|
number = 10
end
puts number
end
end
ABC.new.do_something
···
7stud -- <bbxx789_05ss@yahoo.com> wrote:
1)
class ABC
def do_something
number = 10
puts number
end
end
ABC.new.do_something
--output:--
10
2)
class ABC
def do_something
3.times do |x|
number = 10
end
puts number
end
end
ABC.new.do_something
--output:--
Line 6:in `do_something': undefined local variable or method `number'
for #<ABC:0x401bfaa4> (NameError)
from t.rb:10
Why does ruby get confused by the setter v. local variable assignment
when adding a block?
--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.
Thanks. I got confused by the error message--it lead me to believe the
problem was a setter v. variable assignment parsing problem. Instead it
was a beginner scope problem.
Ok, the following code exhibits the problem I was trying to demonstrate:
class Roulette
def method_missing(name, *args)
person = name.to_s.capitalize
3.times do
number = rand(10) + 1
puts "#{number}..."
end
#puts "#{person} got a #{number}"
end
end
Roulette.new.xxx
If I uncomment the commented line, I get an infinite loop. Why? I'm
pretty sure that is a local variable assignment v. setter problem.
It's not a problem - the issue is that number is scoped only to the 3.times
do block. That means that by the time you get to the puts, number is already
out of scope. Thus Ruby looks for the next alternative which is a instance
method by the name of number - not finding one it ends up back at
method_missing and thus the loop of death ensues.
Try this instead
class Roulette
def method_missing(name, *args)
person = name.to_s.capitalize
number = nil #you now have "number" scoped at the method level instead of
the block level
3.times do
number = rand(10) + 1
puts "#{number}..."
end
puts "#{person} got a #{number}"
end
end
Roulette.new.xxx
John
···
On Fri, Feb 25, 2011 at 4:12 PM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:
As a rule of thumb, variables exist until you reach the "end" or
closing brace of the innermost block that they're still contained in.
In your case, you have this:
def do_something
3.times do |x|
number = 10
end
puts number
end
The local variable `number` is created with the value 10 and then
immediately discarded. This happens three times, since the block
executes three times.
The goal of the next statement is to print a local variable called
`number`. But there is no such local variable in this block. So Ruby
rightfully complains that you didn't define it.
Actually, I think the parser determines that 'number' in the line:
puts number
is a method. See this code:
class Dog
def method_missing(name, *args)
puts "*** #{name.to_s} ***"
super
end
def test
3.times do
number = 10
puts number
end
puts number
end
end
Dog.new.test
--output:--
10
10
10
*** number ***
Line 5:in `method_missing': undefined local variable or method `number'
for #<Dog:0x401bf798> (NameError)
from t.rb:14:in `test'
from t.rb:18
The parser does not think `number` is a method by virtue of writing
"puts number". It tries to invoke a method called number because there
is no local variable named number, because the local variable that you
named `number` is no longer in scope.
To see this for yourself, consider this snippet:
number = 10
def number
20
end
puts number
# => 10
Notice Ruby correctly picks the local variable named `number`,
not the method called `number`.
On Fri, Feb 25, 2011 at 19:40, 7stud -- <bbxx789_05ss@yahoo.com> wrote:
John Feminella wrote in post #983784:
As a rule of thumb, variables exist until you reach the "end" or
closing brace of the innermost block that they're still contained in.
In your case, you have this:
def do_something
3.times do |x|
number = 10
end
puts number
end
The local variable `number` is created with the value 10 and then
immediately discarded. This happens three times, since the block
executes three times.
The goal of the next statement is to print a local variable called
`number`. But there is no such local variable in this block. So Ruby
rightfully complains that you didn't define it.
Actually, I think the parser determines that 'number' in the line:
puts number
is a method. See this code:
class Dog
def method_missing(name, *args)
puts "*** #{name.to_s} ***"
super
end
def test
3.times do
number = 10
puts number
end
puts number
end
end
Dog.new.test
--output:--
10
10
10
*** number ***
Line 5:in `method_missing': undefined local variable or method `number'
for #<Dog:0x401bf798> (NameError)
from t.rb:14:in `test'
from t.rb:18