Julia 變量的作用域

2018-08-12 21:26 更新

變量的作用域

變量的作用域是變量可見的區(qū)域。變量作用域能幫助避免變量命名沖突。

作用域塊是作為變量作用域的代碼區(qū)域。變量的作用域被限制在這些塊內(nèi)部。作用域塊有:

  • function 函數(shù)體(或語法
  • while 循環(huán)體
  • for 循環(huán)體
  • try
  • catch
  • let
  • type

注意 begin 塊不能引入新作用域塊。

當變量被引入到一個作用域中時,所有的內(nèi)部作用域都繼承了這個變量,除非某個內(nèi)部作用域顯式復(fù)寫了它。將新變量引入當前作用域的方法:

  • 聲明 local xconst x ,可以引入新本地變量
  • 聲明 global x 使得 x 引入當前作用域和更內(nèi)層的作用域
  • 函數(shù)的參數(shù),作為新變量被引入函數(shù)體的作用域
  • 無論是在當前代碼之前或 之后 , x = y 賦值都將引入新變量 x ,除非 x 已經(jīng)在任何外層作用域內(nèi)被聲明為全局變量或被引入為本地變量

下面例子中,循環(huán)內(nèi)部和外部,僅有一個 x 被賦值:

function foo(n)
  x = 0
  for i = 1:n
    x = x + 1
  end
  x
end

julia> foo(10)
10

下例中,循環(huán)體有一個獨立的 x ,函數(shù)始終返回 0 :

function foo(n)
  x = 0
  for i = 1:n
    local x
    x = i
  end
  x
end

julia> foo(10)
0

下例中,x 僅存在于循環(huán)體內(nèi)部,因此函數(shù)在最后一行會遇到變量未定義的錯誤(除非 x 已經(jīng)聲明為全局變量):

function foo(n)
  for i = 1:n
    x = i
  end
  x
end

julia> foo(10)
in foo: x not defined

在非頂層作用域給全局變量賦值的唯一方法,是在某個作用域中顯式聲明變量是全局的。否則,賦值會引入新的局部變量,而不是給全局變量賦值。

不必在內(nèi)部使用前,就在外部賦值引入 x

function foo(n)
  f = y -> n + x + y
  x = 1
  f(2)
end

julia> foo(10)
13

上例看起來有點兒奇怪,但是并沒有問題。因為在這兒是將一個函數(shù)綁定給變量。這使得我們可以按照任意順序定義函數(shù),不需要像 C 一樣自底向上或者提前聲明。這兒有個低效率的例子,互遞歸地驗證一個正數(shù)的奇偶:

even(n) = n == 0 ? true  :  odd(n-1)
odd(n)  = n == 0 ? false : even(n-1)

julia> even(3)
false

julia> odd(3)
true

Julia 內(nèi)置了高效的函數(shù) isevenisodd 來驗證奇偶性。

由于函數(shù)可以先被調(diào)用再定義,因此不需要提前聲明,定義的順序也可以是任意的。

在交互式模式下,可以假想有一層作用域塊包在任何輸入之外,類似于全局作用域:

julia> for i = 1:1; y = 10; end

julia> y
ERROR: y not defined

julia> y = 0
0

julia> for i = 1:1; y = 10; end

julia> y

10

前一個例子中,y 僅存在于 for 循環(huán)中。后一個例子中,外部聲明的 y 被引入到循環(huán)中。由于會話的作用域與全局作用域差不多,因此在循環(huán)中不必聲明 global y。但是,不在交互式模式下運行的代碼,必須聲明全局變量。

多變量可以使用以下語法聲明為全局:

function foo()
    global x=1, y="bar", z=3
end

julia> foo()
3

julia> x
1

julia> y
"bar"

julia> z
3

let 語句提供了另一種引入變量的方法。let 語句每次運行都會聲明新變量。let 語法接受由逗號隔開的賦值語句或者變量名:

let var1 = value1, var2, var3 = value3
    code
end

let x = x 是合乎語法的,因為這兩個 x 變量不同。它先對右邊的求值,然后再引入左邊的新變量并賦值。下面是個需要使用 let 的例子:

Fs = cell(2)
i = 1
while i <= 2
  Fs[i] = ()->i
  i += 1
end

julia> Fs[1]()
3

julia> Fs[2]()
3

兩個閉包的返回值相同。如果用 let 來綁定變量 i

Fs = cell(2)
i = 1
while i <= 2
  let i = i
    Fs[i] = ()->i
  end
  i += 1
end

julia> Fs[1]()
1

julia> Fs[2]()
2

由于 begin 塊并不引入新作用域塊,使用 let 來引入新作用域塊是很有用的:

julia> begin
         local x = 1
         begin
           local x = 2
         end
         x
       end
ERROR: syntax: local "x" declared twice

julia> begin
         local x = 1
         let
           local x = 2
         end
         x
       end
1

第一個例子,不能在同一個作用域中聲明同名本地變量。第二個例子,let 引入了新作用域塊,內(nèi)層的本地變量 x 與外層的本地變量 x 不同。

For 循環(huán)及 Comprehensions

For 循環(huán)及 Comprehensions 有特殊的行為:在其中聲明的新變量,都會在每次循環(huán)中重新聲明。因此,它有點兒類似于帶有內(nèi)部 let 塊的 while 循環(huán):

Fs = cell(2)
for i = 1:2
    Fs[i] = ()->i
end

julia> Fs[1]()
1

julia> Fs[2]()
2

for 循環(huán)會復(fù)用已存在的變量來迭代:

i = 0
for i = 1:3
end
i  # here equal to 3

但是, comprehensions 與之不同,它總是聲明新變量:

x = 0
[ x for x=1:3 ]
x  # here still equal to 0

常量

const 關(guān)鍵字告訴編譯器要聲明常量:

const e  = 2.71828182845904523536
const pi = 3.14159265358979323846

const 可以聲明全局常量和局部常量,最好用它來聲明全局常量。全局變量的值(甚至類型)可能隨時會改變,編譯器很難對其進行優(yōu)化。如果全局變量不改變的話,可以添加一個 const 聲明來解決性能問題。

本地變量則不同。編譯器能自動推斷本地變量是否為常量,所以本地常量的聲明不是必要的。

特殊的頂層賦值默認為常量,如使用 functiontype 關(guān)鍵字的賦值。

注意 const 僅對變量的綁定有影響;變量有可能被綁定到可變對象(如數(shù)組),這個對象仍能被修改。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號