Why can I refer to a variable outside of an if/unless/case statement that never ran?
It's because of how the Ruby parser works. Variables are defined by the parser, which walks through the code line-by-line, regardless of whether it will actually be executed.
Once the parser sees x =
, it defines the local variable x
(with value nil
) henceforth in the current scope. Since if
/unless
/case
/for
/while
do not create a new scope, x
is defined and available outside the code block. And since the inner block is never evaluated as the conditional is false, x
is not assigned to (and is thus nil
).
Here's a similar example:
defined?(x) and x = 0
x #=> nil
Note that this is a rather high-level overview of what happens, and isn't necessarily exactly how the parser works.
Why does Ruby seem to hoist variable declarations from inside a case statement even if that code path is not executed?
When the Ruby parser sees the sequence identifier, equal-sign, value,
as in this expressionx = 1
it allocates space for a local variable called
x
. The creation of the
variable—not the assignment of a value to it, but the internal
creation of a variable—always takes place as a result of this kind of
expression, even if the code isn’t executed! Consider this example:if false
x = 1
end
p x # Output: nil
p y # Fatal Error: y is unknownThe assignment to
x
isn’t executed, because it’s wrapped in a failing
conditional test. But the Ruby parser sees the sequencex = 1
, from
which it deduces that the program involves a local variablex
. The
parser doesn’t care whetherx
is ever assigned a value. Its job is
just to scour the code for local variables for which space needs to
be allocated. The result is thatx
inhabits a strange kind of variable limbo.
It has been brought into being and initialized tonil
.
In that respect, it differs from a variable that has no existence at
all; as you can see in the example, examiningx
gives you the valuenil
, whereas trying to inspect the non-existent variabley
results
in a fatal error. But althoughx
exists, it has not played any role in
the program. It exists only as an artifact of the parsing process.
Well-Grounded Rubyist chapter 6.1.2
Ruby: Variables defined in If/else statement are accessible outside of if/else?
Variables are local to a function, class or module defintion, a proc
, a block.
In ruby if
is an expression and the branches don't have their own scope.
Also note that whenever the parser sees a variable assignment, it will create a variable in the scope, even if that code path isn't executed:
def test
if false
a = 1
end
puts a
end
test
# ok, it's nil.
It's bit similar to JavaScript, though it doesn't hoist the variable to the top of the scope:
def test
puts a
a = 1
end
test
# NameError: undefined local variable or method `a' for ...
So even if what you were saying were true, it still wouldn't be nil
.
Ruby variable assignment for not evaluated lines
Before your Ruby code can be run, it must first be parsed, and it's at this stage that the behavior you're experiencing originates.
As the parser scans through the code, whenever it encounters a declaration (foo = 'something'
) it allocates space for that variable by setting its value to nil
. Whether that variable declaration is actually executed in the context of your code is irrelevant. For example:
if false
foo = 42
end
p foo
#=> nil
In the above code's logic foo
is never declared, however it's space in memory is recognized and allocated for by Ruby when the code is parsed out.
Hope this helps!
How to access the variables after if-condition when the variable is defined inside the if-condition in python
Your problem appears to be the fact that you are referencing a variable outside of its scope. Essentially what is happening is in your if statement you are creating a variable exclusively for use within the if scope. Effectively when you have said print vn.firstChild.nodeValue
you can also imagine it as being any other variable such as print undefinedVar
. What is occuring is your are referencing (calling) upon the variable before it has even been defined.
However, no worries here since this is very easy to fix. What we can do is simply create your vn and test variables outside of the if scope, hence inside your actual method by doing the following:
vn = None
test = None
for DO in range(count) :
atnnames = doc.getElementsByTagName("atnId")[DO]
atn = atnnames.childNodes[0].nodeValue
if atn == line[0]:
vn = doc.getElementsByTagName("vn")[DO]
vncontent = vn.childNodes[0].nodeValue
y = vncontent.encode('utf-8')
# print y
if '-' in y:
slt = (int(y.split('-')[0][-1]) + 1)
test = y.replace(y.split('-')[0][-1], str(slt))
# print test
else:
slt = (int(y.split('.')[-1]) + 1)
test = y.replace(y.split('.')[-1], str(slt))
# print test
else:
#print test
vn.firstChild.nodeValue = test
print vn.firstChild.nodeValue
This basically just creates an empty variable in the outermost scope. I've set the values to None
since they get defined once your for loop runs. So what happens now is you have a variable which has been declared outside, and is None
at the start, but as you run your for loop you are not creating a temporary variable just inside the if statement, but you are actually changing the value of
Strange local variable behavior in Ruby
Ruby determines the lifetime of local variables while it parses the code, so even if params = 1
assignment wouldn't be reached, params
will be interpreted as local variable (and set to nil
by default) in this scope.
Here's the link to documentation:
http://docs.ruby-lang.org/en/2.1.0/syntax/assignment_rdoc.html#label-Local+Variables+and+Methods
Ruby lexical scope inside iterator block
I will answer quoting a book by A.Black: Well Grounded Rubyist, Chapter 6, p. 158. (second edition 2014):
When the Ruby parser sees the sequence identifier, equal-sign, and value, as in this expression,
a = 123
it allocates space for a local variable a
. The creation of the variable - not the assignment of a value to it, but the internal creation of a variable - always takes place as a result of this kind of expression, event if the code isn't executed.
I don't understand ruby local scope
There a couple of things going on here. First, variables declared inside the if
block have the same local scope as variables declared at the top level of the method, which is why bar
is available outside the if
. Second, you're getting that error because bob
is being referenced straight out of the blue. The Ruby interpreter has never seen it and never seen it initialized before. It has, however, seen bar
initialized before, inside the if statement. So when is gets to bar it knows it exists. Combine those two and that's your answer.
Related Topics
How to Track System-Specific Config Files in a Repo/Project
Why Doesn't Ruby Support Method Overloading
Best Way to Escape and Unescape Strings in Ruby
Block Definition - Difference Between Braces and Do-End
What Does the "Map" Method Do in Ruby
How to Change My Ruby Version Using Rvm
How to Dynamically Create a Local Variable
How to Avoid Nomethoderror For Missing Elements in Nested Hashes, Without Repeated Nil Checks
Ruby Array Access 2 Consecutive(Chained) Elements At a Time
No Such File to Load - Rubygems (Loaderror)
Installing Bootstrap 3 on Rails App