Local variable acessible outside block - ruby

I am reading about ruby scopes, and it says that local variables aren't accessible outside the block they are defined in.
For instance, in the example below. b shouldn't be accessible outside the if block
a = 3
while a<10 do
if a<5
b= 3
end
a+=1
end
puts b
But for some reason, I can access the b outside the block it was defined in

In Ruby, a "block" is basically code that is enclosed either inside a do...end or between curly braces. (There are other ways — sort of — to create blocks in Ruby, but this definition will hold you for quite a while.) if...end isn't considered a block. Neither is while...end. To further complicate the issue, the do that you add after the while doesn't make it a block, either; Ruby just helpfully ignores it.
Cary has already explained this in the comments, but I'll see if I can clarify it a bit for you. I'll simplify and clean up your code a bit:
a = 3
while a < 10
b = a * 3
a += 1
end
puts b
This will give 27. But now, if I do this:
a = 3
loop do
b = a * 3
a += 1
break if a == 10
end
puts b
I'll get this:
undefined local variable or method `b' for main:Object (NameError)
which is what you were expecting. This is because while is a statement. while...end may enclose several lines of code, but it doesn't count as a block. (Ditto for if.) But loop is actually a method that takes a block as an argument, so the scoping rules that Cary describes in his comment apply.

Related

Using loop counter as part of a variable's name in Ruby [duplicate]

This question already has answers here:
In Ruby, is there no way to dynamically define a local variable in the current context? [duplicate]
(3 answers)
Closed 7 years ago.
I'm trying to generate a bunch of variables that will be operated on. Started with 2 variables, 4, 8,16 and its about time I put the variable generation in a loop.
The variable is acting as a storage for the index of an array.
So a thread wakes up, picks up a variable and says "variable_0" is the first index, and stores location 24 inside of it.
This is usually done like so. This variable goes on and is used multiple times later on.
variable_0 = get_index(threadid)
Once I have 16 of such variables, its a pain and ugly to see that line repeated more than once. So I thought I'd put in a loop and tried it with 2 methods.
1. EVAL
for i in 0..15 do
eval("variable_#{i} = get_index(threadid)")
end
Does not work at all.
2. instance_variable_set
for i in 0..15 do
index_name = "variable_#{i}"
index_val = get_index(threadid)
instance_variable_set("#{index_name}", :index_val)
end
This doesn't work either, since a later statement prints "variable not found " pretty much.
Is there a good way to go about doing this?
Due to restrictions later down the line, I cannot use an array for this, each variable has to be constructed as a single variable, so no arrays or anything like that would work
As others have pointed out it is not possible to create local variables dynamically in Ruby, you could set up a binding as well if you're looking for another method of achieving this.
With eval
b = binding
10.times do |i|
eval("var#{i} = 'foo'", b)
end
> eval("var1", b)
=> "foo"
> eval("local_variables", b)
=> [:var9, :var8, :var7, :var6, :var5, :var4, :var3, :var2, :var1, :var0, :b, :_]
Without eval
b = binding
10.times do |i|
b.local_variable_set("var#{i}", 'foo')
end
> b.local_variable_get('var1')
=> "foo"
> b.local_variables
=> [:var9, :var8, :var7, :var6, :var5, :var4, :var3, :var2, :var1, :var0, :b, :_]
Your second example is almost valid. You forgot # for instance variable name and you passed a symbol as value instead the value itself.
for i in 0..15 do
index_val = "some value #{i}"
instance_variable_set("#variable_#{i}", index_val)
end
puts #variable_4 # "some value"
https://repl.it/BnLO/6

Variable scope in Ruby

Consider the following two snippets of ruby code.
puts "One"
if false
d = 1
end
puts "Two"
puts d
puts "Three"
This prints the following
One
Two
Three
Now, consider the following
[].each do |i|
flag = false
end
puts "Two"
puts flag
puts "Three"
This gives the following
Two
'<main>': undefined local variable or method 'flag' for main:Object (NameError)
Why is it that in the first case a blank is printed and the 2nd case an error is thrown ?
Thanks
The difference is that the if block isn't actually a separate scope like it is in other languages such as Java. Variables declared within the if block have the same scope as the surrounding environment. Now, in your case, that if block won't actually be executed, so you'd normally expect d to be undefined (resulting in the same error you got in the second example). But ruby is a little "smrt" in that the interpreter will set up a variable with that label the moment it sees it, regardless whether it is actually executed, because it essentially doesn't know just yet whether that branch will indeed execute. This is explained in "The Ruby Programming Language" by David Flanagan and Yukihiro Matsumoto (can't copy paste the text, adding screenshot instead):
In the case of the .each loop, that do...end you've written in is actually a block, and it does have its own local scope. In other words, variables declared within a block are local to that block only.
However, blocks "inherit" the scope of the environment in which they're declared, so what you can do is declare flag outside of the .each iteration block, and then the block will be able to access it and set its value. Note that in the example you've given, that won't happen because you're attempting to iterate an empty array, but at the very least you won't receive an error any more.
Some additional reading:
Block variable scope in ruby
Understanding scopes in ruby
In Ruby when you do an assignment to a variable (in your case it's d) anywhere in False-branch of If-statement it declares this variable unless method d= is defined. Basically b = bla-bla-bla in False-branch makes this: b = nil.
When you use block on an empty array nothing happens. And if an array is not empty variable still be local for current iteration of a block unless it was defined outside block scope, for example:
[1,2,3,4].each do |i|
a=i
end
puts a
NameError: undefined local variable or method `a' for main:Object
a=1
[1,2,3,4].each do |i|
a=i
end
puts a
4
Also you have an option to use a as a local one inside a block if it has been previously defined:
a=1
[1,2,3,4].each do |i; a|
a=i
end
puts a
1

What is the purpose of |element| in Ruby array operations syntax?

[1,2,3,4,5,6,7].delete_if{|i| i < 4 }
For example, in the above, why do you need to put |i| before i < 4?
I'm new to Ruby programming and the purpose of this element escapes me.
This is very basic Ruby syntax for a block. A block can sometimes take parameters which are given between the bars |. In your case:
[1,2,3,4,5,6,7].delete_if { |i| i < 4 }
The delete_if method for the type Array accepts a block as a parameter. When the bock is given, the block accepts the array element as a parameter. So it iterates i over each value within the array in this case. More specifically, an element will be deleted from the array if that element is < 4. The result will be:
[4,5,6,7]
You'll often see documentation for methods for Ruby types which say, for example:
delete_if { |item| block } -> Array
Which means that the method accepts a block with a parameter, the block being some code that uses the parameter, and the output being another array. The method's description explains more detail in the documentation (e.g., Ruby Array).
I recommend reading some Ruby getting started information online or a good introductory book which will explain this in more detail.
You have to put i there for the same reason you would put i in the first line here:
def plus_one(i)
return i + 1
end
You have to name your method argument, which you later use as a local variable in the method.
Ruby blocks are similar to methods, they can also receive arguments, and syntax for declaring them is slightly different: enclosing them in | |.
I've redone my answer, even though the OP's question has already been answered, because I thought of a new way to explain this that may help future SO users with the same question.
From high school algebra, you should remember functions like this: f(x) = x + 1.
Imagine putting curly braces around the x + 1: f(x) = { x + 1 }
Then move the (x) to inside the curly braces: f = {(x) x + 1 }
And then get rid of the name f: {(x) x + 1 }. This makes it an "anonymous function," i.e. a "lambda."
Here's the problem: The braces could contain arbitrary statements, which may themselves use parentheses: (x + 1) * 4. So how would Ruby know that the (x) is supposed to be an argument to the function, and not an expression to execute? Some other syntax had to be used. Hence the vertical bars: |x|. (At least I assume that was the thought process).
So {|i| i > 4 } is just like f(i) = i > 4, except that it has no name and is not defined in advance, so the parameter has to be defined "inside" the function itself, rather than being outside attached to the name.
Array#delete_if expects such a function (called a "block" when it's used like this) and knows what to do with it. It passes each member of the array [1,2,3,4,5,6,7] into the block as the argument i to see whether i > 4 is true for that member. It's equivalent to doing something like this:
def greater_than_four(x)
x > 4
end
arr = [1,2,3,4,5,6,7]
arr.each do |el|
arr.delete(el) if greater_than_four(el)
end
You could avoid defining the greater_than_four method in advance by defining a lambda on the fly like this:
arr = [1,2,3,4,5,6,7]
arr.each do |el|
arr.delete(el) if lambda{|i| i > 4}.call(el)
end
But since Array#delete_if already expects a block, and already knows to call it on each element, you can save yourself a whole lot of code:
[1,2,3,4,5,6,7].delete_if{|i| i < 4 }
The parameter which you are passing to the delete_if method is a block and the thing inside the parameter you pass to the block.
Think of the block as a method of sorts. The delete_if method iterates over the block and passes the current item as the parameter i to the block. If the condition evaluates to true then that element gets deleted.

Understanding Ruby Closures

I'm trying to better understand Ruby closures and I came across this example code which I don't quite understand:
def make_counter
n = 0
return Proc.new { n = n + 1 }
end
c = make_counter
puts c.call # => this outputs 1
puts c.call # => this outputs 2
Can someone help me understand what actually happens in the above code when I call c = make_counter? In my mind, here's what I think is happening:
Ruby calls the make_counter method and returns a Proc object where the code block associated with the Proc will be { n = 1 }. When the first c.call is executed, the Proc object will execute the block associated with it, and returns n = 1. However, when the second c.call is executed, doesn't the Proc object still execute the block associated with it, which is still { n = 1 }? I don't get why the output will change to 2.
Maybe I'm not understanding this at all, and it would be helpful if you could provide some clarification on what's actually happening within Ruby.
The block is not evaluated when make_counter is called. The block is evaluated and run when you call the Proc via c.call. So each time you run c.call, the expression n = n + 1 will be evaluated and run. The binding for the Proc will cause the n variable to remain in scope since it (the local n variable) was first declared outside the Proc closure. As such, n will keep incrementing on each iteration.
To clarify this further:
The block that defines a Proc (or lambda) is not evaluated at initialization - the code within is frozen exactly as you see it.
Ok, the code is actually 'evaluated', but not for the purpose of changing the frozen code. Rather, it is checked for any variables that are currently in scope that are being used within the context of the Proc's code block. Since n is a local variable (as it was defined the line before), and it is used within the Proc, it is captured within the binding and comes along for the ride.
When the call method is called on the Proc, it will execute the 'frozen' code within the context of that binding that had been captured. So the n that had been originally been assigned as 0, is incremented to 1. When called again, the same n will increment again to 2. And so on...
I always feel like to understand whats going on, its always important to revisit the basics. No one ever answered the question of what is a Proc in Ruby which to a newbie reading this post, that would be crucial and would help in answering this question.
At a high-level, procs are methods that can be stored inside variables.
Procs can also take a code block as its parameter, in this case it took n = n + 1. In other programming languages a block is called a closure. Blocks allow you to group statements together and encapsulate behavior.
There are two ways to create blocks in Ruby. The example you provide is using curly braces syntax.
So why use Procs if you can use methods to perform the same functionality?
The answer is that Procs give you more flexibility than methods. With Procs you can store an entire set of processes inside a variable and then call the variable anywhere else in your program.
In this case, Proc was written inside a method and then that method was stored inside a variable called c and then called with puts each time incrementing the value of n.
Similar to Procs, Lambdas also allow you to store functions inside a variable and call the method from other parts of a program.
This here:
return Proc.new { n = n + 1 }
Actually, returns a proc object which has a block associated with it. And Ruby creates a binding with blocks! So the execution context is stored for later use and hence why we can increment n. Let me go a bit further into explaining Ruby Closures, so you can have a more broader idea.
First, we need to clarify the technical term 'binding'. In Ruby, a binding object encapsulates the execution context at some particular scope in a program and retains this context for future use in the program. This execution context includes arguments passed to a method and any local variables defined in the method, any associated blocks, the return stack and the value of self. Take this example:
class SomeClass
def initialize
#ivar = 'instance variable'
end
def m(param)
lvar = 'local variable'
binding
end
end
b = SomeClass.new.m(100) { 'block executed' }
=> #<Binding:0x007fb354b7aca0>
eval "puts param", b
=> 100
eval "puts lvar", b
=> local variable
eval "puts yield", b
=> block executed
eval "puts self", b
=> #<SomeClass:0x007fb354ad82e8>
eval "puts #ivar", b
instance variable
The last statement might seem a little tricky but it's not. Remember binding holds execution context for later use. So when we invoke yield, it is invoking yield as if it was still in that execution context and hence it invokes the block.
It's interesting, you can even reassign the value of the local variables in the closure:
eval "lvar = 'changed in eval'", b
eval "puts lvar", b
=> changed in eval
Now this is all cute, but not so useful. Bindings are really useful as it pertains to blocks. Ruby associates a binding object with a block. So when you create a proc or a lambda, the resulting Proc object holds not just the executable block but also bindings for all the variables used by the block.
You already know that blocks can use local variables and method arguments that are defined outside the block. In the following code, for example, the block associated with the collect iterator uses the method argument n:
# multiply each element of the data array by n
def multiply(data, n)
data.collect {|x| x*n }
end
puts multiply([1,2,3], 2) # Prints 2,4,6
What is more interesting is that if the block were turned into a proc or lambda, it could access n even after the method to which it is an argument had returned. That's because there is a binding associated to the block of the lambda or proc object! The following code demonstrates:
# Return a lambda that retains or "closes over" the argument n
def multiplier(n)
lambda {|data| data.collect{|x| x*n } }
end
doubler = multiplier(2) # Get a lambda that knows how to double
puts doubler.call([1,2,3]) # Prints 2,4,6
The multiplier method returns a lambda. Because this lambda is used outside of the scope in which it is defined, we call it a closure; it encapsulates or “closes over” (or just retains) the binding for the method argument n.
It is important to understand that a closure does not just retain the value of the variables it refers to—it retains the actual variables and extends their lifetime. Another way to say this is that the variables used in a lambda or proc are not statically bound when the lambda or proc is created. Instead, the bindings are dynamic, and the values of the variables are looked up when the lambda or proc is executed.

Ruby forgets local variables during a while loop?

I'm processing a record-based text file: so I'm looking for a starting string which constitutes the start of a record: there is no end-of-record marker, so I use the start of the next record to delimit the last record.
So I have built a simple program to do this, but I see something which suprises me: it looks like Ruby is forgetting local variables exist - or have I found a programming error ? [although I don't think I have : if I define the variable 'message' before my loop I don't see the error].
Here's a simplified example with example input data and error message in comments:
flag=false
# message=nil # this is will prevent the issue.
while line=gets do
if line =~/hello/ then
if flag==true then
puts "#{message}"
end
message=StringIO.new(line);
puts message
flag=true
else
message << line
end
end
# Input File example:
# hello this is a record
# this is also part of the same record
# hello this is a new record
# this is still record 2
# hello this is record 3 etc etc
#
# Error when running: [nb, first iteration is fine]
# <StringIO:0x2e845ac>
# hello
# test.rb:5: undefined local variable or method `message' for main:Object (NameError)
#
From the Ruby Programming Language:
(source: google.com)
Blocks and Variable Scope
Blocks define a new variable scope: variables created within a block exist only within that block and are undefined outside of the block. Be cautious, however; the local variables in a method are available to any blocks within that method. So if a block assigns a value to a variable that is already defined outside of the block, this does not create a new block-local variable but instead assigns a new value to the already-existing variable. Sometimes, this is exactly the behavior we want:
total = 0
data.each {|x| total += x } # Sum the elements of the data array
puts total # Print out that sum
Sometimes, however, we do not want to alter variables in the enclosing scope, but we do so inadvertently. This problem is a particular concern for block parameters in Ruby 1.8. In Ruby 1.8, if a block parameter shares the name of an existing variable, then invocations of the block simply assign a value to that existing variable rather than creating a new block-local variable. The following code, for example, is problematic because it uses the same identifier i as the block parameter for two nested blocks:
1.upto(10) do |i| # 10 rows
1.upto(10) do |i| # Each has 10 columns
print "#{i} " # Print column number
end
print " ==> Row #{i}\n" # Try to print row number, but get column number
end
Ruby 1.9 is different: block parameters are always local to their block, and invocations of the block never assign values to existing variables. If Ruby 1.9 is invoked with the -w flag, it will warn you if a block parameter has the same name as an existing variable. This helps you avoid writing code that runs differently in 1.8 and 1.9.
Ruby 1.9 is different in another important way, too. Block syntax has been extended to allow you to declare block-local variables that are guaranteed to be local, even if a variable by the same name already exists in the enclosing scope. To do this, follow the list of block parameters with a semicolon and a comma-separated list of block local variables. Here is an example:
x = y = 0 # local variables
1.upto(4) do |x;y| # x and y are local to block
# x and y "shadow" the outer variables
y = x + 1 # Use y as a scratch variable
puts y*y # Prints 4, 9, 16, 25
end
[x,y] # => [0,0]: block does not alter these
In this code, x is a block parameter: it gets a value when the block is invoked with yield. y is a block-local variable. It does not receive any value from a yield invocation, but it has the value nil until the block actually assigns some other value to it. The point of declaring these block-local variables is to guarantee that you will not inadvertently clobber the value of some existing variable. (This might happen if a block is cut-and-pasted from one method to another, for example.) If you invoke Ruby 1.9 with the -w option, it will warn you if a block-local variable shadows an existing variable.
Blocks can have more than one parameter and more than one local variable, of course. Here is a block with two parameters and three local variables:
hash.each {|key,value; i,j,k| ... }
Contrary to some of the other answers, while loops don't actually create a new scope. The problem you're seeing is more subtle.
Prerequisite knowledge: a brief scoping demo
To help show the contrast, blocks passed to a method call DO create a new scope, such that a newly assigned local variable inside the block disappears after the block exits:
### block example - provided for contrast only ###
[0].each {|e| blockvar = e }
p blockvar # NameError: undefined local variable or method
But while loops (like your case) are different, because a variable defined in the loop will persist:
arr = [0]
while arr.any?
whilevar = arr.shift
end
p whilevar # prints 0
Summary of the "problem"
The reason you get the error in your case is because the line that uses message:
puts "#{message}"
appears before any code that assigns message.
It's the same reason this code raises an error if a wasn't defined beforehand:
# Note the single (not double) equal sign.
# At first glance it looks like this should print '1',
# because the 'a' is assigned before (time-wise) the puts.
puts a if a = 1
Not Scoping, but Parsing-visibility
The so-called "problem" - i.e. local-variable visibility within a single scope - is due to ruby's parser. Since we're only considering a single scope, scoping rules have nothing to do with the problem. At the parsing stage, the parser decides at which source locations a local variable is visible, and this visibility does not change during execution.
When determining if a local variable is defined (i.e. defined? returns true) at any point in the code, the parser checks the current scope to see if any code has assigned it before, even if that code has never run (the parser can't know anything about what has or hasn't run at the parsing stage). "Before" meaning: on a line above, or on the same line and to the left-hand side.
An exercise to determine if a local is defined (i.e. visible)
Note that the following only applies to local variables, not methods. (Determining whether a method is defined in a scope is more complex, because it involves searching included modules and ancestor classes.)
A concrete way to see the local variable behavior is to open your file in a text editor. Suppose also that by repeatedly pressing the left-arrow key, you can move your cursor backward through the entire file. Now suppose you're wondering whether a certain usage of message will raise the NameError. To do this, position your cursor at the place you're using message, then keep pressing left-arrow until you either:
reach the beginning of the current scope (you must understand ruby's scoping rules in order to know when this happens)
reach code that assigns message
If you've reached an assignment before reaching the scope boundary, that means your usage of message won't raise NameError. If you don't reach any assignment, the usage will raise NameError.
Other considerations
In the case a variable assignment appears in the code but isn't run, the variable is initialized to nil:
# a is not defined before this
if false
# never executed, but makes the binding defined/visible to the else case
a = 1
else
p a # prints nil
end
While loop test case
Here's a small test case to demonstrate the oddness of the above behavior when it happens in a while loop. The affected variable here is dest_arr.
arr = [0,1]
while n = arr.shift
p( n: n, dest_arr_defined: (defined? dest_arr) )
if n == 0
dest_arr = [n]
else
dest_arr << n
p( dest_arr: dest_arr )
end
end
which outputs:
{:n=>0, :dest_arr_defined=>nil}
{:n=>1, :dest_arr_defined=>nil}
{:dest_arr=>[0, 1]}
The salient points:
The first iteration is intuitive, dest_arr is initialized to [0].
But we need to pay close attention in the second iteration (when n is 1):
At the beginning, dest_arr is undefined!
But when the code reaches the else case, dest_arr is visible again, because the interpreter sees that it was assigned 2 lines above.
Notice also, that dest_arr is only hidden at the start of the loop; its value is never lost (as evidenced by the final contents being [0, 1]).
This also explains why assigning your local before the while loop fixes the problem. The assignment doesn't need to be executed; it only needs to appear in the source code.
Lambda example
f1 = ->{ f2 }
f2 = ->{ f1 }
p f2.call()
# The following fails because the body of f1 tries to access f2 before an assignment for f2 was seen by the parser.
p f1.call() # undefined local variable or method `f2'.
Fix this by putting an f2 assignment before f1's body. Remember, the assignment doesn't actually need to be executed!
f2 = nil # Could be replaced by: if false; f2 = nil; end
f1 = ->{ f2 }
f2 = ->{ f1 }
p f2.call()
p f1.call() # ok
Method-masking gotcha
Things get really hairy if you have a local variable with the same name as a method:
def dest_arr
:whoops
end
arr = [0,1]
while n = arr.shift
p( n: n, dest_arr: dest_arr )
if n == 0
dest_arr = [n]
else
dest_arr << n
p( dest_arr: dest_arr )
end
end
Outputs:
{:n=>0, :dest_arr=>:whoops}
{:n=>1, :dest_arr=>:whoops}
{:dest_arr=>[0, 1]}
A local variable assignment in a scope will "mask"/"shadow" a method call of the same name. (You can still call the method by using explicit parentheses or an explicit receiver.) So this is similar to the previous while loop test, except that instead of becoming undefined above the assignment code, the dest_arr method becomes "unmasked"/"unshadowed" so that the method is callable w/o parentheses. But any code after the assignment will see the local variable.
Some best-practices we can derive from all this
Don't name local variables the same as method names in the same scope
Don't put the initial assignment of a local variable inside the body of a while or for loop, or anything that causes execution to jump around within a scope (calling lambdas or Continuation#call can do this too). Put the assignment before the loop.
I think this is because message is defined inside the loop. At the end of the loop iteration "message" goes out of scope. Defining "message" outside of the loop stops the variable from going out of scope at the end of each loop iteration. So I think you have the right answer.
You could output the value of message at the beginning of each loop iteration to test whether my suggestion is correct.
Why do you think this is a bug? The interpreter is telling you that message may be undefined when that particular piece of code executes.
I'm not sure why you're surprised: on line 5 (assuming that the message = nil line isn't there), you potentially use a variable that the interpreter has never heard of before. The interpreter says "What's message? It's not a method I know, it's not a variable I know, it's not a keyword..." and then you get an error message.
Here's a simpler example to show you what I mean:
while line = gets do
if line =~ /./ then
puts message # How could this work?
message = line
end
end
Which gives:
telemachus ~ $ ruby test.rb < huh
test.rb:3:in `<main>': undefined local variable or method `message' for main:Object (NameError)
Also, if you want to prepare the way for message, I would initialize it as message = '', so that it's a string (rather than nil). Otherwise, if your first line doesn't match hello, you end up tring to add line to nil - which will get you this error:
telemachus ~ $ ruby test.rb < huh
test.rb:4:in `<main>': undefined method `<<' for nil:NilClass (NoMethodError)
you can simply do this :
message=''
while line=gets do
if line =~/hello/ then
# begin a new record
p message unless message == ''
message = String.new(line)
else
message << line
end
end
# hello this is a record
# this is also part of the same record
# hello this is a new record
# this is still record 2
# hello this is record 3 etc etc

Resources