一、lua閉包
若將乙個函式寫在另外乙個函式內部,那麼這個位於內部的函式便可以訪問外部函式中的區域性變數,這個特性稱之為closure,中文翻譯為「閉包」。
二、實踐分析閉包的實現
(一)、例項1:lua指令逆向分析
function newcounter()
local i = 0
return function()
i = i+1
return i
endendc1= newcounter()
print(c1())
print(c1())
chunkspry互動模式反編譯:
>function newcounter() local i = 0 return function() i = i+1; return i end end
; source chunk: (interactive mode)
; x86 standard (32-bit, little endian, doubles)
; function [0] definition (level 1)
; 0 upvalues, 0 params, 2 stacks
.function 0 0 2 2
.const "newcounter" ; 0
; function [0] definition (level 2)
; 0 upvalues, 0 params, 2 stacks
.function 0 0 0 2
.local "i" ; 0
.const 0 ; 0
; function [0] definition (level 3) //匿名函式:closure
; 1 upvalues, 0 params, 2 stacks
.function 1 0 0 2
.upvalue "i" ; 0
.const 1 ; 0
[1] getupval 0 0 ; i //r(0):=upvalue(0)
[2] add 0 0 256 ; 1 //i=i+1
[3] setupval 0 0 ; i //upvalue(0):=r(0),將暫存器0中的值寫回upvalue(0)
[4] getupval 0 0 ; i //再次取出upvalue(0),為解下來的返回i做準備
[5] return 0 2
[6] return 0 1
; end of function
[1] loadk 0 0 ; 0
[2] closure 1 0 ; 1 upvalues
[3] move 0 0
[4] return 1 2
[5] return 0 1
; end of function
[1] closure 0 0 ; 0 upvalues
[2] setglobal 0 0 ; newcounter
[3] return 0 1
; end of function
>
從上述lua位元組碼可以看出,區域性變數在閉包函式中是通過upvalue傳遞的。
在匿名函式中,第一條指令[1]操作是從該函式的upvalue列表中,0號索引位置上得到區域性變數i,並拷貝在第0號暫存器中。
在執行期間,upvalue是由closure設定並由虛擬機器維護的。
在pascal中,外層作用域中的變數是通過遍歷棧幀找到的。然而,lua函式是第一類值,可以被賦值給變數在別處引用。lua虛擬機器的解決辦法是通過
getupval 和setupval 提供訪問upvalue的乾淨介面,不過upvalue的管理是虛擬機器自己處理的。
事實上,lua編譯乙個函式時,會為他生成乙個原型(prototype),其中包含了函式體對應的虛擬機器指令、函式用到的常量值(數,文字字串等等)和一些除錯資訊。
在執行時,每當lua執行乙個形如function...end 這樣的表示式時,他就會建立乙個新的資料物件,其中包含了相應函式原型的引用、環境(environment,用來查詢全域性變數的表)的引用及乙個由所有upvalue引用組成的陣列,而這個資料物件就稱為
閉包
。由此可見,函式是編譯期概念,是靜態的,而閉包是執行期概念,是動態的。
upvalue實際是區域性變數,而區域性變數是儲存在函式堆疊框架上(stack frame)的,所以只要upvalue還沒有離開自己的作用域,他就一直生存在函式堆疊上。這種情況下,閉包將通過指向堆疊上的upvalue的引用來訪問他們,一旦upvalue即將離開自己的作用域(這也意味著他馬上要從堆疊中消失),閉包就會為他分配空間並儲存當前的值,以後便可通過指向新分配空間的引用來訪問該upvalue。
(二)、例項2:
function func1(n)
local function func2()
print(n)
endn = n + 1000
return func2
endg1=func1(1)
g1() //列印出1還是1001?
func1將返回內部定義的乙個函式
g1=func1(1),使g1得到了第一類值的函式
g1()這條語句實質上執行了一次這個函式,那麼print(n)這條語句列印的英國是1呢還是1001呢?為什麼呢?
通過執行程式,我們發現奇怪的問題:
內嵌函式定義在n = n + 1000這條語句之前,可為什麼g1()列印出的卻是10001?
upvalue實際是區域性變數,而區域性變數是儲存在函式堆疊框架上(stack frame)的,所以只要upvalue還沒有離開自己的作用域,他就一直生存在函式堆疊上。這種情況下,閉包將通過指向堆疊上的upvalue的引用來訪問他們,一旦upvalue即將離開自己的作用域(這也意味著他馬上要從堆疊中消失),閉包就會為他分配空間並儲存當前的值,以後便可通過指向新分配空間的引用來訪問該upvalue。當執行到g1(),既func1(1)的n = n + 1000時,閉包已建立了,不過n並沒有離開作用域,所以閉包仍然引用堆疊上的n,當return func2完成時,n即將結束生命,此時閉包便將n(已是1001了)複製到自己管理的空間中以便將來訪問。弄清晰了內部的秘密後,執行結果就不難解釋了。
三、閉包的設計
lua源**閉包部分欣賞
未完待續...
C 語言特性中的效能分析
這是一本關於c 效能優化的書,比較不錯,叫 c 應用程式效能優化 第2版 c 語言特性中的效能分析 大多數開發人員通常都有這個觀點,即組合語言和c語言適合用來編寫對效能要求非常高的程式。而c 語言的主要應用範圍是編寫複雜度非常高,但是對 效能要求不是那麼嚴格的程式。因為在大多數人看來,c 語言相對前...
Lua中的表 讀《Lua設計與實現》筆記
1.lua語言用表來表示一切資料結構。2.lua表分為陣列和雜湊表部分。陣列部分索引從1開始。雜湊表部分可以儲存任何不能儲存在陣列部分的資料,唯一的要求是鍵值不能為nil lobject.h typedef struct table node value型別定義 typedef struct lua...
Lua 語言中的點 冒號與self
lua程式設計中,經常遇到函式的定義和呼叫,有時候用點號呼叫,有時候用冒號呼叫,這裡簡單的說明一下原理。girl function girl.gotomarket girl somemoney girl.money girl.money somemoney endgirl.gotomarket gi...