我們知道lua腳本語言的變量是弱類型的,即變量沒有類型,值纔有類型,同一名稱的變量具體類型要看所賦值的類型,如下
a=1 --整型
a=1.0 --浮點型
a="ab" --string型
a={} --table型
a=function() ... end --function型
全局變量和局部變量,類似於shell腳本
全局變量:顧名思義,其生命有效期是全局的,整個lua文件中都可以使用,可以在任意地方定義(函數參數除外),但有個原則,使用時必須是先定義好的,否則就是nil,請看下面的代碼
print(i);
function test(j)
i = 1;
end
test(); --如果不執行test(), i未定義,都是nil
print(i,j);
執行結果是
nil
1 nil
局部變量:只在某些特定的範圍內有效的變量,稱爲局部變量,用local修飾。最主要的局部變量是定義在函數內部的局部變量。請看下面的代碼片段
local j=2; --雖然是定義的local變量,但卻是在函數外部,
--所以其作用域是它之後的整個文件,所以等同於全局變量
function test()
local i = 1; --局部變量,只在test函數內部有效
print("j="..j);
end
test();
print(i,j);
執行結果爲:
j=2
nil 2
函數閉包
函數閉包:閉包(closure),通過調用含有一個內部函數加上該外部函數持有的外部局部變量(upvalue)的外部函數(就是工廠)產生的一個實例函數
閉包組成:外部函數+外部函數創建的upvalue+內部函數(閉包函數)
這裏,test是外部函數, local i是upvalue, function() i=i+1.. 是內部函數
function test()
local i=0 --局部變量
return function() --尾調用,即尾部調用,將另一函數作爲function類型返回
i = i+1
return i
end
end
c1=test()
c2=test(); --c1,c2是建立在同一函數,同一局部變量的不同實例上的兩個不同的閉包
--閉包中的upvalue各自獨立,調用一次test()就會產生一個新的閉包
print(c1()) -->1
print(c1()) -->2//重複調用時每一個調用都會記住上一次調用後的值,就是說i=1了已經被記住了
print(c2()) -->1//閉包不同所以upvalue不同
print(c2()) -->2
函數嵌套,即函數內部定義了另一個函數,注意不是函數調用。因爲lua將函數也看成是一種基本數據類型,可以用來做賦值和返回。實際也是秉承了c語言指針概念的基礎上做的轉換,將函數的地址作爲基本數據類型(個人理解,不確定正確,待考證)。
非局部變量,即上面提到的upvalue,這個值改如何理解,如果你有C語言基礎,我們不妨先將它理解爲函數內部定義的“靜態(static)變量”,但upvalue它有自己的特點。
首先看upvalue的定義要求,一定是外部函數定義的的local局部變量,且在內部函數調用過。如下圖中 j n k都是upvalue, 而h由於在內部函數中未調用,所以不是upvalue。(全局變量的作用域是全局的,所以談論全局變量做upvalue無意義)
function newCounter() --外部函數
local j = 0; local n = 0;
local k = 0; local h = 0;
print("f1:",j,k,n); --cd1
return function() --內部函數
print("f2:",j,k,n); --cd2
j = n;
k = n;
n = n+1;
return j,k,n;
end
end
counter = newCounter(); --cd3
print("f3:",counter()); --cd4
print("----------分割線----------");
print("f4:",counter()); --cd5
執行結果
f1: 0 0 0
f2: 0 0 0
f3: 0 0 1
----------分割線----------
f2: 0 0 1
f4: 1 1 2
說明:cd3(code3)將函數賦值給counter,產生一個新實例,即生成一個閉包。從結果看cd4和cd5兩次調用counter, 但外部函數的cd1只執行了一次,所以可以看出,閉包函數的外部函數部分只在第一次調用時執行,後面再次時調用直接跳到內部函數執行,所以upvalue的j k n的值只在第一次由外部函數初始化給定,後續的值仍然以某種形式“存活”下來,因此這個特性有點類似於c語言的靜態變量。
如果上面的嵌套函數再次賦值給counter1,則會生成一個新的閉包實例,它和之前的counter之間是相互獨立的關係,我們看下面的代碼
function newCounter()
local j = 0;local n = 0;local k = 0; local h = 0;
print("f1:",j,k,n);
return function()
print("f2:",j,k,n);
k = n;
j = n;
n = n+1;
return j,k,n;
end
end
counter = newCounter();
print("f3:",counter());
print("----------分割線----------");
print("f4:",counter());
print("**********分割線**********");
counter1 = newCounter();
print("f5:",counter1());
print("----------分割線----------");
print("f6:",counter1());
執行結果:
f1: 0 0 0
f2: 0 0 0
f3: 0 0 1
----------分割線----------
f2: 0 0 1
f4: 1 1 2
**********分割線**********
f1: 0 0 0
f2: 0 0 0
f5: 0 0 1
----------分割線----------
f2: 0 0 1
f6: 1 1 2
說明: * * * * * * *分割線 * * * * * * *,上面是counter的執行結果,下面是counter1的,兩次執行結果一樣,證明counter和counter1之間是獨立的。我們可以藉助c++的概念來理解,將閉包函數看做是創建類(class)的實例,counter和counter1分別是相同類(class)的兩個實例,之間沒有關聯關係,而upvalue則是對應的實例成員函數內部的靜態變量。
upvalue非局部變量可以使用debug.getupvalue和debug.setupvalue來獲取和設置,具體函數說明如下:
- getupvalue (f, up):此函數返回函數 f 的第 up 個上值的名字和值。 如果該函數沒有那個上值,返回 nil 。 以 ‘(’ (開括號)打頭的變量名錶示沒有名字的變量 (去除了調試信息的代碼塊)
- setupvalue (f, up, value):這個函數將 value 設爲函數 f 的第 up 個上值。 如果函數沒有那個上值,返回 nil 否則,返回該上值的名字
這裏補充一點up的排序規律,我們看下面的代碼
function newCounter()
local j = 0;local n = 0;local k = 0; local h = 0;
local l=0;
print("f1:",j,k,n);
return function()
print("f2:",j,k,n);
l = n;
h = n;
k = n;
j = n;
n = n+1;
return j,k,n;
end
end
counter = newCounter();
local i = 1;
print("-----------------------");
repeat
name, val = debug.getupvalue(counter, i);
if name then
print("index", i, name, "=", val);
if(name == "n") then
debug.setupvalue(counter, 2, 10);
end
i = i + 1;
end
until not name
print("-----------------------");
print("f3:",counter());
執行結果如下:
f1: 0 0 0
-----------------------
index 1 j = 0
index 2 k = 0
index 3 n = 0
index 4 l = 0
index 5 h = 0
-----------------------
f2: 0 10 0
f3: 0 0 1
從結果上看upvalue的順序和內部函數return的順序有關,return 前的排在前面(j k n),沒有return的排在後面,且按照調用順序排列(l h)。
補充一下閉包定義的兩種寫法:
--寫法1
function f1(n)
--local i=0;
return function()
print(n);
return n;
end
end
g1=f1(200);
print(g1()); --如果寫成g1(300),帶入的參數會被忽略
--寫法2
function f3(n)
local i=0;
function f4()
print(n);
return n;
end
return f4;
end
g2=f3(200);
print(g2());
好了,以上這些就是我個人的理解,有不對的請指正,謝謝!