Anonymous functions in for loops

Why does this code snippet print 2 instead of 1?

anon = Array{Any}(undef, 2)
for i = 1:2
  anon[i] = ()-> println(i)
  i += 1
end
anon[1]()
1 Like

This is just how closures work. The closure () -> println(i) closes over the binding i, not just its current value, so it can see changes to that binding. Here’s another example:

julia> function outer()
         i = 1
         inner = () -> println(i)
         i = 2
         return inner
       end
outer (generic function with 1 method)

julia> f = outer()
#7 (generic function with 1 method)

julia> f()
2

Note that this can be confusing because a for-loop introduces a new local variable i at each iteration, so if you do:

julia> fs = []
Any[]

julia> for i in 1:2
         push!(fs, () -> i)
       end

julia> fs[1]()
1

julia> fs[2]()
2

then fs[1] has captured the variable i introduced by the first iteration of the loop, while fs[2] has captured the variable i introduced by the second iteration of the loop.

If the i were the same binding in each iteration of the loop, then you’d get 2 both times due to the for loop incrementing i. In fact, Python does that, which can be very confusing when you switch between Python and Julia:

# Python

>>> fs = []
>>> for i in range(1, 3):
...     fs.append(lambda: i)                                                                                            
... 
>>> fs[0]()
2
>>> fs[1]()
2
8 Likes