I am learning Julia using Ivo Balbaert's book. He uses the following example:
anon = Array{Any}(undef, 2)
for i = 1:2
anon[i] = () -> println(i)
i += 1
end
Now calling the two functions in this array outputs:
julia> anon[1](); anon[2]()
2
3
I fail to understand why the output is 2, 3 instead of 1, 2. At the first pass through the loop i = 1
, so that anon[1] = () -> println(1)
. The author continues:
Here, both
anon[1]
andanon[2]
are anonymous functions. When they are called withanon[1]()
andanon[2]()
, they print2
and3
(the values of i when they were created plus one).
The expected behavior is then achieved by using let
. What I am missing in this explanation, however, is how Julia scoping rules operate in order to produce the first (unexpected) result of 2, 3. In other words, how do the values 2 and 3 come to be? Could someone please explain this? Thanks!
This is pretty tricky. You need to know two things:
for
loop variablei
gets a fresh binding on every iteration (I guess you know it - I do not know Ivo's book, but from your question I guess this is what he is discussing in it)Now to explain the second point have a look at the following (I assume you have run the code above):
And you can see that
anon[1]
has boxed the binding toi
that was present in the first iteration of the loop. As in the second loop the binding toi
is freshanon[2]
has a reference to this fresh binding.You can even access this memory location like this:
or even like this (not recommended):
Finally note that within a single iteration of the loop assigning to variable
i
does not change the binding (you are writing to the same memory location).