Hi all. I have a very simple code snippet and I can’t figure out why I get this error:
Simple text file to read: test.dat contains
Fred 50
Mike 51
Ida 52
Code:
f = open("test.dat")
i = 1
for ln in eachline(f)
if i == 1
desigA, numA = ln[1:8],ln[9:end]
else
desigB, numB = ln[1:8],ln[9:end]
end
if i > 1
println(desigA, desigB)
end
i+= 1
end
I know I could work with regex, split, readdlm etc., but it has a certain reason why I use slicing of the string (fixed column format) and I think it is not connected to the problem I am pointing here.
Running this example code (with Julia 0.6.3) I got the error, that desigA is not defined (after iteration 1). Why?
ERROR: LoadError: UndefVarError: desigA not defined
Stacktrace:
[1] macro expansion at /home/mike/astwork/ast/astapp/moid/jul/test.jl:14 [inlined]
[2] anonymous at ./:?
Thanks, Mike
Variable is out of scope when the loop finishes would be my guess.
1 Like
To elaborate on what @cormullion said: you can simplify to reproduce the same problem with
for i in 1:3
if i == 1
desigA = true
else
desigB = true
end
if i > 1
println(desigA, desigB)
end
end
2 Likes
The name desigA
only lives inside the first if
clause. If you want it to be visible from the function scope, give it some value in that scope:
for (i, ln) in enumerate(eachline(f))
desigA = desigB = ""
if i == 1
desigA, numA = ln[1:8],ln[9:end]
else
desigB, numB = ln[1:8],ln[9:end]
end
if i > 1
println(desigA, desigB)
end
end
No desigA
does NOT only live inside the if
. It does, however, only live in each iteration of the for
. It is correct that in order for it to live outside the for
loop you have to declare it in that (function or global) scope, though you code does not do that.
You also don’t need to give it a value, just use local desigA, desigB
in the function scope is enough if the code is actually in a function. Otherwise, the outer scope of the loop will be the global scope and you need to use global desigA, desigB
inside the loop body to make sure the global variables are used.
1 Like
There’s no faster way to get a right answer than posting the wrong one! Thanks for the insights.
But the print command is inside the loop. The same code as Python works like I expect it:
f = open("test.dat")
i = 1
for ln in f:
if i == 1:
desigA,numA = ln[0:7],ln[8:]
else:
desigB,numB = ln[0:7],ln[8:]
if i > 1:
print(desigA, desigB)
i+= 1
python test.py
Fred Mike
Fred Ida
Julia’s variable-scoping is different from Python’s in this instance: variables inside the for loop are freshly allocated at each iteration, and when an if
clause isn’t evaluated for a particular iteration, any variable that’s only defined by that clause won’t exist for that iteration.
https://docs.julialang.org/en/v0.6.2/manual/variables-and-scoping/#For-Loops-and-Comprehensions-1
edit: per the manual, while
blocks behave like Python for
blocks in this respect:
i = 1
while !eof(f)
ln = readline(f)
if i == 1
desigA, numA = ln[1:8],ln[9:end]
else
desigB, numB = ln[1:8],ln[9:end]
end
if i > 1
println(desigA, desigB)
end
i += 1
end
1 Like
Wow, this is strange IMHO. Using the for loop my code cannot work, even with the globale/local keyword it doesn’t work because after the first iteration the value of desigA is lost forever. desigA (and numA) is set by the first line of the data file. Of course it can be read and set by a single statement before the loop.
Your solution with the while loop works like the Python code (BTW: and like I expected it).
Thanks for this good explanation and demonstrative example!
Reading the first line before the loop body is a lot cleaner, and it sidesteps the scoping issue:
ln = readline(f)
desigA, numA = ln[1:8],ln[9:end]
for ln in eachline(f)
desigB, numB = ln[1:8],ln[9:end]
println(desigA, desigB)
end
It appears that you did not add the local or global to the right place.
Please don’t give a workaround to a non-issue. This is obviously not the right solution to solve the scope issue.
1 Like
I think I had the global/local statements at the right place but I didn’t defined the variables in the outer scope (you wrote: “You also don’t need to give it a value,…”), it was a misunderstanding by me. It is not necessary to give them a value, but necessary to “declare” them, e.g. desigA = … = “”. And neither in the global scope nor in the function scope it seems to be necessary to use global desigA, desigB or local local desigA, desigB.
So JUST putting the line: desigA = numA = desigB = numB = “” in the initial code I have posted is sufficient to fix the problem. Doesn’t matter if this script is in the global context or you put everything into a function, then you have also to initialize this variables (outside the loop).
desigA = numA = desigB = numB = ""
#function loop()
#desigA = numA = desigB = numB = ""
f = open("test.dat")
i = 1
for ln in eachline(f)
if i == 1
desigA, numA = ln[1:8], ln[9:end]
else
desigB, numB = ln[1:8], ln[9:end]
end
if i > 1
println("$desigA $desigB")
end
i+= 1
end
#end
#loop()
This is giving them values. Declaring them is just
If declaring works, of course assigning unnecessary values also works. If you don’t mind the extra work, that’s certainly fine.
They don’t work the same and that’s why the global version is deprecated on 0.7.
No you don’t have to, just declaring the variable is fine.
1 Like
OK, thanks for the explanations. Now I got it right both using the global and the local scope variant.