Simple usage question reading a file


#1

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


#2

Variable is out of scope when the loop finishes would be my guess.


#3

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

#4

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

#5

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.


#6

There’s no faster way to get a right answer than posting the wrong one! Thanks for the insights.


#7

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    

#8

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

#9

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!


#10

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

#11

It appears that you did not add the local or global to the right place.


#12

Please don’t give a workaround to a non-issue. This is obviously not the right solution to solve the scope issue.


#13

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()


#14

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.


#15

OK, thanks for the explanations. Now I got it right both using the global and the local scope variant.