Easily grasp the scope of Julia

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 globallogo:

a = 1
for i=1:2
    global a
    a += 1
end

Got it a = 3. The interesting thing is that the globalstatement 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 globalmust 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: globalcan 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 letgrammar documentation.

If you just treat the global variable as a loop variable, you don't need to add a globalmark, 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 nerror 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 globallogo 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...endcan 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.

Guess you like

Origin blog.csdn.net/iamzhtr/article/details/93388930