Variables and Scoping
Until now, we have simply used variables without any explanation. Julia’s usage of variables closely resembles that of other dynamic languages, so we have hopefully gotten away with this liberty. In what follows, however, we address this oversight and provide details of how variables are used, declared, and scoped in Julia.The scope of a variable is the region of code within which a variable is visible. Variable scoping helps avoid variable naming conflicts. The concept is intuitive: two functions can both have arguments called x without the two x‘s referring to the same thing. Similarly there are many other cases where different blocks of code can use the same name without referring to the same thing. The rules for when the same variable name does or doesn’t refer to the same thing are called scope rules; this section spells them out in detail.
Certain constructs in the language introduce scope blocks, which are regions of code that are eligible to be the scope of some set of variables. The scope of a variable cannot be an arbitrary set of source lines, but will always line up with one of these blocks. The constructs introducing such blocks are:
- function bodies (either syntax)
- while loops
- for loops
- try blocks
- catch blocks
- let blocks
- type blocks.
Certain constructs introduce new variables into the current innermost scope. When a variable is introduced into a scope, it is also inherited by all inner scopes unless one of those inner scopes explicitly overrides it. These constructs which introduce new variables into the current scope are as follows:
- A declaration local x introduces a new local variable.
- A declaration global x makes x in the current scope and inner scopes refer to the global variable of that name.
- A function’s arguments are introduced as new local variables into the function’s body scope.
- An assignment x = y introduces a new local variable x only if x is neither declared global nor explicitly introduced as local by any enclosing scope, before or after the current line of code.
function foo(n)
x = 0
for i = 1:n
x = x + 1
end
x
end
julia> foo(10)
10
function foo(n)
x = 0
for i = 1:n
local x
x = i
end
x
end
julia> foo(10)
0
function foo(n)
for i = 1:n
x = i
end
x
end
julia> foo(10)
in foo: x not defined
One last example shows that an outer assignment introducing x need not come before an inner usage:
function foo(n)
f = y -> n + x + y
x = 1
f(2)
end
julia> foo(10)
13
even(n) = n == 0 ? true : odd(n-1)
odd(n) = n == 0 ? false : even(n-1)
julia> even(3)
false
julia> odd(3)
true
Since functions can be used before they are defined, as long as they are defined by the time they are actually called, no syntax for forward declarations is necessary, and definitions can be ordered arbitrarily.
At the interactive prompt, variable scope works the same way as anywhere else. The prompt behaves as if there is scope block wrapped around everything you type, except that this scope block is identified with the global scope. This is especially apparent in the case of assignments:
julia> for i = 1:1; y = 10; end
julia> y
y not defined
julia> y = 0
0
julia> for i = 1:1; y = 10; end
julia> y
10
The let statement provides a different way to introduce variables. Unlike assignments to local variables, let statements allocate new variable bindings each time they run. An assignment modifies an existing value location, and let creates new locations. This difference is usually not important, and is only detectable in the case of variables that outlive their scope via closures. The let syntax accepts a comma-separated series of assignments and variable names:
let var1 = value1, var2, var3 = value3
code
end
let a = b, c = d
body
end
Fs = cell(2);
for i = 1:2
Fs[i] = ()->i
end
julia> Fs[1]()
2
julia> Fs[2]()
2
Fs = cell(2);
for i = 1:2
let i = i
Fs[i] = ()->i
end
end
julia> Fs[1]()
1
julia> Fs[2]()
2
julia> begin
local x = 1
begin
local x = 2
end
x
end
syntax error: local x declared twice
julia> begin
local x = 1
let
local x = 2
end
x
end
1
Constants
A common use of variables is giving names to specific, unchanging values. Such variables are only assigned once. This intent can be conveyed to the compiler using the const keyword:const e = 2.71828182845904523536
const pi = 3.14159265358979323846
Local constants are quite different. The compiler is able to determine automatically when a local variable is constant, so local constant declarations are not necessary for performance purposes.
Special top-level assignments, such as those performed by the function and type keywords, are constant by default.
Note that const only affects the variable binding; the variable may be bound to a mutable object (such as an array), and that object may still be modified.
No comments:
Post a Comment
Thank you