If I have:
2.times do
i ||= 1
print "#{i} "
i += 1
print "#{i} "
end
I get 1 2 1 2
, whereas I was expecting 1 2 2 3
. Why does i
lose its assignment when the loop starts over? It behaves as expected if the assignment occurs outside the loop, so I guess it has to do with scope, but I didn't realize loops have their own scopes. Can someone clarify?
Update: Thanks for the help on this. Part of my confusion stemmed from coming to Ruby from Python, which doesn't have block scope (I think).
In Java, the scope of a local variable is the body of the method in which it is declared. In other words, the variable is visible in the body of the method where its declaration appears, but it is not visible on the outside the method.
The scope of local variables always remains inside constructor, method, and block. It can not be accessible from outside the constructor, method, and block. 3. The scope of instance variables is within the class.
Example 1: Local Scope Variable In the above program, variable a is a global variable and variable b is a local variable. The variable b can be accessed only inside the function greet . Hence, when we try to access variable b outside of the function, an error occurs.
The scope of a variable is the area of the program where the variable is valid. A global variable is valid from the point it is declared to the end of the program. A local variable's scope is limited to the block where it is declared and cannot be accessed (set or read) outside that block.
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.
Look at the code below:
2.times do
p defined? i
i ||= 1
p defined? i
p "#{i} "
i += 1
p "#{i} "
end
Output:
nil
"local-variable"
"1 "
"2 "
nil
"local-variable"
"1 "
"2 "
That means in each iteration a new scope is created,and i
is known to that scope only; which is proved by nil
and "local-variable"
.
Now i
is created outside of block
, and see the output(no nil
comes):
i = nil
2.times do
p defined? i
i ||= 1
p defined? i
p "#{i} "
i += 1
p "#{i} "
end
Output:
"local-variable"
"local-variable"
"1 "
"2 "
"local-variable"
"local-variable"
"2 "
"3 "
To know more about ||=
look What Ruby’s ||= (Double Pipe / Or Equals) Really Does
It isn't the "loop" that has a scope. It's the block. Yes, a block is a local scope.
If you do not want a variable to be understood as local to the block, it needs to exist outside the block beforehand. Even just setting i
to nil in a preceding line would do this.
(But your expectation of 1 2 3 4
will still not quite be met...!)
You can have some fun with it. Say for example you want to access the scope inside the block.
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"
This is important because it allows developers to basically blow away the encapsulation of a block, and should be used with caution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With