Julia的范围详细说明:在循环内定义闭包

时间:2018-12-06 14:33:29

标签: julia

我正在使用Ivo Balbaert's book学习Julia。他使用以下示例:

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

现在调用此数组输出中的两个函数:

julia> anon[1](); anon[2]()
2
3

我不明白为什么输出是2、3而不是1、2。在第一次通过循环i = 1时,使anon[1] = () -> println(1)如此。作者继续:

  

在这里,anon[1]anon[2]都是匿名函数。当他们是   用anon[1]()anon[2]()调用,它们打印23(   我创建它们时加上一个。

然后使用let实现预期的行为。但是,我在此解释中缺少的是Julia范围规则如何操作才能产生2(3)的第一个(意外的)结果。换句话说,值2和3是如何变成的?有人可以解释一下吗?谢谢!

1 个答案:

答案 0 :(得分:2)

这非常棘手。您需要知道两件事:

  • for循环变量i中,每次迭代都会获得新的绑定(我想您知道吗-我不知道Ivo的书,但是从您的问题中我想这就是他正在讨论的内容在其中)
  • 在Julia闭包中,是通过创建一个对象来实现的,该对象从外部范围捕获变量,然后可以访问它

现在要解释第二点,请看以下内容(我假设您已经运行了上面的代码):

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

julia> anon[1].i.contents
2

您会看到anon[1]已将循环的第一次迭代中存在的对i的绑定装箱。就像在第二个循环中一样,i的绑定是新鲜的anon[2]对此新鲜绑定的引用。

您甚至可以像这样访问此存储位置:

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

julia> anon[1]()
100

甚至是这样(不推荐):

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

最后请注意,在循环的一次迭代中,分配给变量i不会更改绑定(您正在写入相同的内存位置)。