Users migrating from Matlab will find Julia's scope a bit fascinating. To be honest, I also made a few bends. In order to avoid bringing the reader into the whirlpool of concepts, this article lists a few typical situations to help readers quickly solve the scope problem.
The scope of the loop body
The scope of Matlab includes a global domain and the local domain of each function. Julia is relatively more cumbersome, with a global domain and many types of local domains. The loop body is a kind of local domain, for example:
a = 1
for i=1:2
a += 1
end
An error will be reported ERROR: LoadError: UndefVarError: a not defined
, proving that variables in the global domain are not automatically inherited into the local domain. In fact, Julia can intelligently (zhi) can (zhang) determine which variables are automatically inherited, the rules are:
- If the global variable is not modified in the local domain, that is, it never appears on the left side of the equal sign, then it will be automatically inherited. You can use global variables to do anything you want, as long as you don't modify it.
- Conversely, if the global variable is modified, a local variable with the same name will be created to replace it. Any changes to this local variable will not affect the global variable.
To solve the error, add a global
logo:
a = 1
for i=1:2
global a
a += 1
end
Got it a = 3
. The interesting thing is that the global
statement can be placed anywhere in the local domain.
Multi-layer loops only need to be marked on the outermost layer, for example:
a = 1
for i=1:2
global a
for j=1:3
a += 1
end
end
But it global
must not be placed outside the loop body, it will report an error like this:
a = 1
global a
for i=1:2
for j=1:3
a += 1
end
end
It's really fascinating to look at it this way, just remember it anyway. If you want to force the generalization, it is probably: global
can make the local domain of the loop body "look out", and also make the outer loop "look in", but not make the global domain "look in". If you want to get the bottom line, please refer to the let
grammar documentation.
If you just treat the global variable as a loop variable, you don't need to add a global
mark, because we didn't modify it. E.g:
m = 3; n = 0
for i=1:m
global n
n += 1
end
n
The scope of the function
The local domain of the function is a little more complicated than the loop body, including two cases: (1) The function and the loop body follow the same automatic inheritance rules, and you can directly inherit global variables, as long as you don't modify it in the function. E.g:
n = 1
function g()
return n+1
end
g()
At this time, it should be noted that if there is a loop body in the function, the loop body will divide a new local domain in the local domain of the function. E.g:
m = 3; n = 0
function g(m)
for i=1:m
global n
n += 1
end
return n
end
g(m)
If it is removed, an global n
error will be reported. So beware of loops at all times.
(2) If a variable is passed as a parameter, the function will create a local variable with the same name in the local domain, that is, pass it in the way of "passing by value". The local variable created in this way will not affect the global variable with the same name, which is the same as the above "abandon automatic inheritance". E.g:
t = 1; w = 0
function y(w)
w = t
end
println("y=",y(w)," w=",w)
The output is:
julia> include("Demo.jl")
y=1 w=0
Of course, the global
logo can also be used here, for example:
t = 1; w = 0
function y()
global w
w = t
end
println("y=",y()," w=",w)
The output is:
julia> include("Demo.jl")
y=1 w=1
but! ! ! If the passed parameters are more complex, then the function will wisely change the transfer strategy from "passing by value" to "passing by address", that is, passing a pointer, and any modification will directly affect the original address. For example, pass in a structure array:
struct atom
u
v
end
w = Array{atom}(undef,2,2)
function y(w)
w[1,2] = atom(0.1,0.2)
w
end
println("y[1]=",y(w)[1,2]," w[1]=",w[1,2])
w
The output is:
julia> include("DemoParallel_1.jl")
y[1]=atom(0.1, 0.2) w[1]=atom(0.1, 0.2)
2×2 Array{atom,2}:
#undef atom(0.1, 0.2)
#undef #undef
It can be seen that it is indeed a reference rather than a value. What's interesting is that when adopting the reference strategy, it will ignore the rules and force inheritance, for example:
struct atom
u
v
end
n = 10
w = Array{atom}(undef,2,2)
function y()
w[1,2] = atom(0.1,0.2)
w
end
println("y[1]=",y()[1,2]," w[1]=",w[1,2])
w
After testing, Julia will treat structures and structure arrays as "complex", and treat ordinary numbers, numeric arrays, and shared arrays as "simple", regardless of scale. Give an example of a structure:
mutable struct atom
u
v
end
w = atom(0.1,0.2)
function y(w)
w.u = 0.5
w
end
println("y[1]=",y(w).u," w[1]=",w.u)
The scope of begin...end
There is a special syntax that begin...end
can put several expressions together. E.g:
w = begin
w = 1
w += 0.1
end
It is actually equivalent to the w = ( w = 1; w += 0.1)
latter's branch writing. This structure has no local domain.
The scope of the module
This is not nonsense, it has the same meaning in other languages.