问题
(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
This code compiles and runs in Julia 0.6, but in 1.0 produces ERROR: UndefVarError: k not defined
.
Did something change between versions? What's wrong with this code in Julia 1.0?
回答1:
The answer by shadowtalker is correct. However, there is one important issue with this code that is worth adding some more explanation (and it was too long for a comment).
The relevant scoping rules in Julia for this case are the following (and if you want to read the details you can find them here https://docs.julialang.org/en/latest/manual/variables-and-scoping/#Local-Scope-1):
while
block introduces a new local scope;- local scope inherits variables from enclosing global scope only for reading (unless they are qualified with
global
keyword as explained by shadowtalker); - local scope inherits variables from enclosing local scope for reading and writing unless they are qualified with
local
keyword.
Now - why this is important? The reason is that your code will behave differently if you run it in global scope (e.g. in Julia REPL) and differently if you have it in a local scope (e.g. inside a function).
If you run it in global scope then what shadowtalker showed is needed. However, if yourun it e.g. inside a function you do not have to change anything. For example this function will work correctly:
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
also without creating a function if you e.g. use let
block in a global scope the code will just run as intended:
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
because let
creates a local scope (I have added the statement at the end of let
block to make it evaluate to the values of the introduced variables so that you can inspect them).
This is a significant change from Julia 0.6 and earlier where Julia made a difference between hard and soft local scopes (see here https://docs.julialang.org/en/release-0.6/manual/variables-and-scoping/#scope-of-variables-1), but this distinction is gone - all local scopes now behave identically. This is a significant change an in practice it means that you can expect that the code that runs correctly inside some local scope (a function in most cases) will change its behavior if you copy-paste it to a global scope. From my experience using let
blocks as shown above is the simplest way alleviate this problem.
回答2:
Yes, something changed. I think this is what's going on:
Assigning to p
, q
, and k
inside the loop does actually creates new p
, q
, and k
bindings in a new local scope. Then, because k
is also used on the right-hand side, Julia tries to use the local k
and fails.
In order to obtain the expected semantics (mutating the existing p
, q
, and k
defined at the top level), you need to declare them "global" first:
while k <= BigInt(4)
global p, q, k
(p, q, k) = (k*k, BigInt(2)*k+BigInt(1), k+BigInt(1))
end
edit: thanks to user AnAverageHuman on Freenode for figuring this out.
来源:https://stackoverflow.com/questions/51814689/why-does-this-assignment-inside-a-loop-fail-in-julia-0-7-and-1-0