lua函式
可以被當成引數傳遞,也可以被當成結果返回
,在函式體中仍然可以定義內嵌函式。lua閉包是lua函式生成的資料物件。每個閉包可以有乙個upvalue值,或者多個閉包共享乙個upvalue數值。
如果函式f2定義在函式f1中,那麼f2為f1的內嵌函式,f1為f2的外包函式,外包和內嵌都具有傳遞性,即f2的內嵌必然是f1的內嵌,而f1的外包也一定是f2的外包。
內嵌函式可以訪問外包函式已經建立的區域性變數,而這些區域性變數則稱為該內嵌函式的外部區域性變數(或者upvalue)
**如下:
[cpp]
view plain
copy
function f1(n)
-- 函式引數也是區域性變數
local function f2()
print(n) -- 引用外包函式的區域性變數
end
return
f2
end
g1 = f1(1979)
g1() -- 列印出1979
g2 = f1(500)
g2() -- 列印出500
當執行完g1 = f1(1979)後,區域性變數n的生命本該結束,但因為它已經成了內嵌函式f2的upvalue,它又被賦給了變數g1,所以它仍然能以某種形式繼續「存活」下來,從而令g1()列印出正確的值。
lua編譯乙個函式時,其中包含了函式體對應的虛擬機器指令、函式用到的常量值(數,文字字串等等)和一些除錯資訊。在執行時,每當lua執行乙個形如function...end 這樣的函式時,它就會建立乙個新的資料物件,其中包含了相應函式原型的引用、環境(用來查詢全域性變數的表)的引用以及乙個由所有upvalue引用組成的陣列,而這個資料物件就稱為閉包。由此可見,函式是編譯期概念,是靜態的,而閉包是執行期概念,是動態的。g1和g2的值嚴格來說不是函式而是閉包,並且是兩個不相同的閉包,而這兩個閉包保有各自的upvalue值。
使用upvalue**如下:
[cpp]
view plain
copy
function f1(n)
local function f2()
print(n)
end
n = n + 10
return
f2
end
g1 = f1(1979)
g1() -- 列印出1989
g1()列印出來的是1989,原因是列印的是upvalue的值。
upvalue實際是區域性變數,而區域性變數是儲存在函式堆疊框架上的,所以只要upvalue還沒有離開自己的作用域,它就一直生存在函式堆疊上。這種情況下,閉包將通過指向堆疊上的upvalue的引用來訪問它們,一旦upvalue即將離開自己的作用域,在從堆疊上消除之前,閉包就會為它分配空間並儲存當前的值,以後便可通過指向新分配空間的引用來訪問該upvalue。當執行到f1(1979)的n = n + 10時,閉包已經建立了,但是變數n並沒有離開作用域,所以閉包仍然引用堆疊上的n,當return f2完成時,n即將結束生命,此時閉包便將變數n(已經是1989了)複製到自己管理的空間中以便將來訪問。
upvalue還可以為閉包之間提供一種資料共享的機制。
乙個函式建立的閉包共享乙份upvalue。
**如下:
[cpp]
view plain
copy
function create(n)
local function foo1()
print(n)
end
local function foo2()
n = n + 10
end
return
foo1,foo2
end
f1,f2 = create(1979)--建立閉包
f1() -- 列印1979
f2()
f1() -- 列印1989
f2()
f1() -- 列印1999
f1,f2這兩個閉包的原型分別是create中的內嵌函式foo1和foo2,而foo1和foo2引用的upvalue是同乙個,即create的區域性變數n。執行完create呼叫後,閉包會把堆疊上n的值複製出來,那麼是否f1和f2就分別擁有乙個n的拷貝呢?其實不然,當lua發現兩個閉包的upvalue指向的是當前堆疊上的相同變數時,會聰明地只生成乙個拷貝,然後讓這兩個閉包共享該拷貝,這樣任乙個閉包對該upvalue進行修改都會被另乙個探知。上述例子很清楚地說明了這點:每次呼叫f2都將upvalue的值增加了10,隨後f1將更新後的值列印出來。upvalue的這種語義很有價值,它使得閉包之間可以不依賴全域性變數進行通訊,從而使**的可靠性大大提高。同一閉包建立的其他的閉包共享乙份upvalue。
閉包在建立之時其需要的變數就已經不在堆疊上,而是引用更外層外包函式的區域性變數(實際上是upvalue)。
[cpp]
view plain
copy
function test(n)
local function foo()
local function inner1()
print(n)
end
local function inner2()
n = n + 10
end
return
inner1,inner2
end
return
foo
end
t = test(1979)--建立閉包(共享乙份upvalue)
f1,f2 = t()--建立閉包
f1() -- 列印1979
f2()
f1() -- 列印1989
g1,g2 = t()
g1() -- 列印1989
g2()
g1() -- 列印1999
f1() -- 列印1999
執行完t = test(1979)後,test的區域性變數n就結束生命週期了,所以當f1,f2這兩個閉包被建立時堆疊上根本找不到變數n。test函式的區域性變數n不僅是foo的upvalue,也是inner1和inner2的upvalue。t = test(1979)之後,閉包t 已經把n儲存為upvalue,之後f1、f2如果在當前堆疊上找不到變數n就會自動到它們的外包閉包(這裡是t的)的upvalue引用陣列中去找.
g1和g2與f1和f2共享同乙個upvalue。因為g1和g2與f1和f2都是同乙個閉包t 建立的,所以它們引用的upvalue (變數n)實際也是同乙個變數,而它們的upvalue引用都會指向同乙個地方。
Lua 迭代器和閉包
內容來自 lua程式設計 第四版 18.1節 迭代器和閉包。迭代器 iterator 是一種可以讓我們遍歷乙個集合中所有元素的 結構。在 lua 語言中,通常使用函式表示迭代器 每一次呼叫函式時,函式會返回集合中的 下乙個 元素。乙個典型的例子就是io.read,每次呼叫該函式時它都會返回標準輸入中...
Lua筆記5 函式和閉包
多重賦值 1 函式是最後乙個表示式,則保留函式所有的返回值 2 函式沒有返回值或者沒有足夠多的返回值,則nil補充 3 如果函式不是最後乙個表示式,則只保留的第乙個返回值返回值 函式呼叫時,使用圓括號,則只返回乙個值 closure 可以將已有的api重新定義,並進行增強 tail recursio...
lua閉包全面解析
在 lua中,閉包 closure 是由乙個函式和該函式會訪問到的非區域性變數 或者是 upvalue 組成的,其中非區域性變數 non local variable 是指不是在區域性作用範圍內定義的乙個變數,但同時又不是乙個全域性變數,主要應用在巢狀函式和匿名函式裡,因此若乙個閉包沒有會訪問的非區...