談一談Lua中的閉包
在閱讀《Programming in Lua》一書中,第六章第一小節closure(閉包)有些費解,查閱了一些資料後,總結如下。
Functions in Lua are first-class values with proper lexical scoping.
—— Programming in Lua.
在Lua語言中,有兩個很有意思的特性:
- 所有的函數都是匿名函數。可以這樣理解,function實際上是佔據在某個內存位置中,函數名不過是對這一實體的引用(Reference)。如下例:
function foo(x) return 2 * xend
等價於:
foo = function (x) return 2 * xend
可以看到第二種寫法,實際上是定義了一個匿名函數(沒有名稱的函數),然後將這個函數賦給了foo這個變數。
下面看另外一個例子:
function func() return function () print("Hello World") endendlocal f1 = func()print(f1) -- 運行結果 function: 0x41dc6186f1() -- 運行結果 Hello World
上述例子說明了,函數是同樣可以作為另外一個函數的返回值的。測試過程中,第一行返回了函數在內存中的地址,第二行運行了該函數。
- 被嵌套的函數可以訪問它外層的數據,為了說明這個問題,請看這個例子:
function func() local index = 0 local inner = function () print(index) index = index + 1 end inner() -- 結果:0 inner() -- 結果:1 print(index) -- 結果:2endfunc()
上一個例子說明了,外層函數func()中的局部數據index是可以被內層函數inner()訪問和修改的,index也被稱作inner()的upvalue。
結合上述兩點,我們可以得到更多有意思的結論,如這個例子:
function func() local index = 0 print("Hello") return function () print(index) index = index + 1 endendlocal inner = func()print(inner) -- 結果: function: 0037BE88inner() -- 結果: 0inner() -- 結果: 1local othre = func()other() -- 結果: 0other() -- 結果: 1
由此可以看出函數的局部變數是可以保存在函數內部的,通過調用該函數內嵌的函數可以獲取並修改局部變數的值,該函數的局部變數(upvalue)和內嵌函數的組合使用,形成了閉包,這看起來與C++中類的成員變數有些類似。函數具有閉包的功能後,不必再擔心其局部變數沒辦法保存了,這一點對程序員的影響是很大的。
還有另一個例子:
function func() local index = 0 return function () index = index + 1 return index endendlocal inner = func()print(inner()) -- 結果: 1print(inner()) -- 結果: 2
推薦閱讀:
※Lua 有哪些優點,適用於哪些場景?
※陰陽師肝不動了,試試Lua吧
※為什麼很多編程語言用 end 作為區塊結束符,而放棄花括弧?
※維基百科中模板和模塊有什麼區別?
※Lua程序逆向之Luac位元組碼與反彙編