为什么在Julia 0.7和1.0中循环中的此分配失败?

时间:2018-08-13 03:04:19

标签: julia

(k, a, b, a1, b1) = (BigInt(2), BigInt(4), BigInt(1), BigInt(12), BigInt(4))

while k <= BigInt(4)
  (p, q, k) = (k*k, BigInt(2)*k+BigInt(1), k+BigInt(1))
end

此代码可在Julia 0.6中编译并运行,但在1.0中会生成ERROR: UndefVarError: k not defined

两个版本之间是否有所更改? Julia 1.0中的这段代码有什么问题?

2 个答案:

答案 0 :(得分:4)

shadowtalker的回答是正确的。但是,此代码存在一个重要问题,值得增加一些解释(并且注释太长了)。

在此情况下,Julia中的相关范围规则如下(如果您想阅读详细信息,可以在https://docs.julialang.org/en/latest/manual/variables-and-scoping/#Local-Scope-1处找到它们):

  • while块引入了新的本地范围;
  • 局部作用域仅从封闭的全局作用域中继承变量以供读取(除非它们由shadowtalker所说明的global关键字限定);
  • 本地作用域从封闭的本地范围继承变量以进行读写,除非它们用local关键字限定。

现在-为什么这很重要?原因是,如果在全局范围内(例如,在Julia REPL中)运行代码,则代码的行为会有所不同;而在局部范围(例如,在函数内部)中运行时,代码的行为将有所不同。

如果在全局范围内运行它,则需要显示shadowtalker。但是,如果您要在函数内部,您无需更改任何内容。例如,此功能将正常工作:

function f()
    (k, a, b, a1, b1) = (BigInt(2), BigInt(4), BigInt(1), BigInt(12), BigInt(4))

    while k <= BigInt(4)
      (p, q, k) = (k*k, BigInt(2)*k+BigInt(1), k+BigInt(1))
    end
end

也不会创建函数,例如在全局范围内使用let块,代码将按预期运行:

let
    k, a, b, a1, b1 = BigInt(2), BigInt(4), BigInt(1), BigInt(12), BigInt(4)

    while k <= BigInt(4)
      (p, q, k) = (k*k, BigInt(2)*k+BigInt(1), k+BigInt(1))
    end

    k, a, b, a1, b1
end

因为let创建了一个本地范围(我在let块的末尾添加了该语句,使其对所引入变量的值求值,以便您可以检查它们)。

这是从Julia 0.6和更早版本开始的重大更改,在Julia 0.6和朱莉亚之前,Julia在硬本地作用域和软本地作用域之间进行了区别(请参见https://docs.julialang.org/en/release-0.6/manual/variables-and-scoping/#scope-of-variables-1),但是这种区别消失了-所有本地作用域现在的行为都相同。这是一个重大的变化,实际上这意味着您可以期望,如果将其复制粘贴到全局范围内,则在某些本地范围(在大多数情况下为函数)中正确运行的代码将更改其行为。根据我的经验,如上所示,使用let块是缓解此问题的最简单方法。

答案 1 :(得分:2)

是的,有些改变了。我认为这是怎么回事:

在循环内分配给pqk实际上会在以下位置创建新的pqk绑定一个新的本地范围。然后,由于右侧还使用了k,因此Julia尝试使用 local k并失败了。

为了获得预期的语义(对在顶层定义的现有pqk进行突变),您需要首先将它们声明为“ global”:< / p>

while k <= BigInt(4)
  global p, q, k

  (p, q, k) = (k*k, BigInt(2)*k+BigInt(1), k+BigInt(1))
end

编辑:感谢Freenode上的用户AnAverageHuman弄清楚了这一点。