Local scope in for loops AGAIN

Hi,
New to Julia from MATLAB. I’ve been reading up on the local variable scope rules in Julia, but fail to understand the behaviour i see in the following example

Example code

u0 = 10     #The global scope variable

for k in 1:10
    local local_u
    
    #In the first iteration, local_u reads from the global variable
    if k == 1           
        local_u = copy(u0)
    end

    #My main function call goes here - returns uk which i want to use in next iteration
    uk = k*10*local_u

    #Reassigning local_u to the result from the current iteration
    local_u = copy(uk)
    println("At end of iteration ", k, " local_u is ", local_u)
end

I can see that it goes through 1 iteration, but then throws the error when trying to execute.
I get the error message → UndefVarError: local_u not defined

So my Question is - Does the local variable (local_u) get destroyed after every iteration in the for loop ? Or am i misunderstanding something here.

Any help appreciated.

You’re recreating local local_u in every iteration of the loop, thus overwriting its previous value. The behaviour you experience is therefore to be expected.

Why not just wrap it in a function?

u = 10 # global scope variable

function foo(u)
    for k in 1:10
        u = k*10*u # could also write u *= k*10
        println("At the end of iteration $k local u is $u")
    end
end

foo(u)

This is much simpler and you don’t have to deal with the local keyword, reassigning, copying or anything else.

Sorry, i do not fully understand. Then removing this line from my code should have solved th issue right? Even when i remove the line “Local local_u”, i get the same behaviour and error. It should be able to access the variable local_u from iteration k=1 during iteratio k=2 in this case, but doesn’t look like it.

My original code is trying to solve an Optimal Control Problem and simulate the plant, which are already in their own functions. I just put in k*10*local_u as a dummy line here instead of my function call. I am using the for loop to step through time in the main file, and hence did not want to wrap in a seperate function again.

OP, here is an MWE

julia> for i in 1:10
       if i == 1
           a = 2
       end
       
       @show a
       end

This will throw the same error.

The way to think about this is that when you say a = 1, that assignment only exists for the first iteration of the loop. The other iterations of the loop don’t know that a variable a exists since it isn’t created in those other iterations.

If you want it to persist, you need to define it outside the loop. This is certainly a bit of a pain before 1.5 but now it works “as expected” when copied and pasted into the REPL.

My approach would remain the same. Just write u = bar(u) instead of u = k*10*u. I don’t see any problem with that.

Oh, that’s too bad. My mistake was that i misunderstood that the local variables were available until the “for loop” terminates. Thanks for the correction.

In this case, then I will use this approach then,

a = 1
for i in 1:10
    global a1

   a_temp = i*10*a #Get the next step of a. to be used in next iteration
   
    @show a_temp
    a = a_temp     #assigning a_temp to be used in next iteration
    
end

If i may ask a followup question.
The line a1 = a_temp seems to be working instead of me having to use a = copy(a_temp).

Am i right in my understanding then that
a = a_temp binds the variable names to the same underlying number, but when a_temp is destroyed at the end of iteration 1, the global variable a is able to retain the same value and does not end up cleared (even though the underlying local variable was cleared out)

yes that is correct.

Probably one thing more to note, is that arrays work differently than scalars, and the copy function plays an important role there:

julia> a = 1 ; b = [ 1, 1 ] ;

julia>  for i in 1:2
           a2 = a
           b2 = b 
           a2 = 0
           b2[1] = 0
       end

julia> a
1

julia> b
2-element Array{Int64,1}:
 0
 1

Note that the value in the first position of the b vector changes, but the value of a does not, the variable a2 is local to the loop. Now using copy:

julia> a = 1 ; b = [ 1, 1 ] ;

julia>  for i in 1:2
           a2 = copy(a) # this is redudant for a scalar
           b2 = copy(b)
           a2 = 0
           b2[1] = 0
       end

julia> a
1

julia> b
2-element Array{Int64,1}:
 1
 1

julia> b2
ERROR: UndefVarError: b2 not defined



For the scalar, this is the same as before, but now b2 is a vector local to the loop, occupying a different space in memory, and altering its values do not alter the original b.

That isn’t exactly true. Right now:

  • It isn’t required in Jupyter (and it never has been).
  • It (>= 1.5) isn’t required in the (normal) REPL or execute the code through any IDE that sends text to a normal REPL.
  • It is required if you use the REPL in vscode
  • It is required if you run it as a .jl file with include, etc.

My advice: stick to jupyter for this sort of thing where there are any top-level loops, and move to functions as soon as you no longer want to use jupyter

Yes, I fixed that already. In that context it was never required.

What changed is that if you need to assign a new value to the global variable, in 1.5 or greater you do not need to add the global anymore when using the REPL.


julia> a = 0
0

julia> for i in 1:2
          global a # not required anymore in 1.5
          a = a + 1
       end

I really think what you want is:

function calculatemything(a)
   for i in 1:10
      ...
   end
  return stuff
end

calculatemything(1)

I would strongly advice to put your loop into a function, as @fbanning suggested, unless you are looping over very few elements.
The reason for this is that global variables are not type-stable and therefore you are getting bad performance - see the very first performance tip in Performance Tips · The Julia Language