Julia scoping specifics: defining closure within l

2020-04-16 18:13发布

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] and anon[2] are anonymous functions. When they are called with anon[1]() and anon[2](), they print 2 and 3 (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!

标签: julia
1条回答
放荡不羁爱自由
2楼-- · 2020-04-16 18:17

This is pretty tricky. You need to know two things:

  • within a for loop variable i 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)
  • in Julia closures are implemented via a creation of an object that captures a variable from an outer scope and then it can access it

Now to explain the second point have a look at the following (I assume you have run the code above):

julia> anon[1].i
Core.Box(2)

julia> anon[1].i.contents
2

And you can see that anon[1] has boxed the binding to i that was present in the first iteration of the loop. As in the second loop the binding to i is fresh anon[2] has a reference to this fresh binding.

You can even access this memory location like this:

julia> anon[1].i.contents = 100
100

julia> anon[1]()
100

or even like this (not recommended):

julia> for i = 1:2
           anon[i] = () -> println(i)
           anon[i].i.contents = 100 + i
           i += 1
           println(i)
       end
102
103

julia> anon[1]()
102

julia> anon[2]()
103

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

查看更多
登录 后发表回答