lua閉包全面解析

2021-08-14 21:25:10 字數 2887 閱讀 6655

lua中,閉包(

closure

)是由乙個函式和該函式會訪問到的非區域性變數(或者是

upvalue

)組成的,其中非區域性變數(

non-local variable

)是指不是在區域性作用範圍內定義的乙個變數,但同時又不是乙個全域性變數,主要應用在巢狀函式和匿名函式裡,因此若乙個閉包沒有會訪問的非區域性變數,那麼它就是通常說的函式。也就是說,在

lua中,函式是閉包一種特殊情況

。簡而言之,閉包就是乙個函式加乙個upvalue。那麼接下來看下upvalue是啥。

lua使用結構體upvalue來實現閉包。外面的區域性變數可以直接通過upvalue進行訪問。upvalue最開始的時候指向棧中的乙個變數,此時這個變數還在它的生存週期內。當變數離開作用域(譯者注:就是函式返回後,變數的生存週期結束時),這個變數就從棧轉移到了upvalue中。雖然這個變數儲存在upvalue中,但是訪問這個變數還是間接通過upvalue中的乙個指標進行的(譯者注:和在棧中時候的訪問方式一樣)。因此,變數位置的轉移對任何試圖讀寫這個變數的**都是透明的。有別於這個變數在乙個函式內部時候的行為,函式宣告、訪問這個變數,就是直接對棧的操作。看下具體例子:

[cpp]view plain

copy

function f1(n)  

--函式引數n也是區域性變數  

local function f2()  

print(n)   --引用外部函式的區域性變數  

end  

return

f2  

end  

g1 = f1(2015)  

g1() -- 列印出2015  

g2 = f1(2016)  

g2() -- 列印出2016  

這裡的n就是upvalue。

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(2015)  

f1() -- 列印2015  

f2()  

f1() -- 列印2025  

f2()  

f1() -- 列印2035  

上面的例子中,閉包f1和

f2共享同乙個

upvalue

了,這是因為當

lua發現兩個閉包的

upvalue

指向的是當前堆疊上的相同變數時,會聰明地只生成乙個拷貝,然後讓這兩個閉包共享該拷貝,這樣任乙個閉包對該

upvalue

進行修改都會被另乙個探知。為什麼會這樣,我們看下面的解釋:

通過為每個變數最多建立乙個upvalue

並按需要重複利用這個

upvalue

,保證了未決狀態(未超過生命週期)的區域性變數(pending vars

)能夠在閉包之間正確地共享。為了保證這種唯一性,

lua維護這一條鍊錶,該鍊錶中每個節點對應乙個開啟的

upvalue

(opend upvalue

)結構,開啟的

upvalue

是指當前正指向棧區域性變數的

upvalue

,如上圖的未決狀態的區域性變數鍊錶(

the pending vars list

)。當lua

建立乙個新的閉包時,

lua會遍歷當前函式所有的外部的區域性變數,對於每乙個外部的區域性變數,若在上面的鍊錶中能找到該變數,則重複使用該開啟的

upvalue

,否則,

lua會建立乙個新的開啟的

upvalue

,並把它插入鍊錶中。當區域性變數離開作用域時(即超過變數生命週期),這個開啟的upvalue

就會變成關閉的

upvalue

(closed upvalue

),並把它從鍊錶中刪除,一旦某個關閉的

upvalue

不再被任何閉包所引用,那麼它的儲存空間就會被**。

最後看下閉包的應用。閉包最常用的乙個應用就是

實現迭代器。所謂迭代器就是一種可以遍歷一種集合中所謂元素的機制。每個迭代器都需要在每次成功呼叫之間保持一些狀態,這樣才能知道它所在的位置及如何進到下乙個位置。閉包剛好適合這種場景。比如下面的**:

[cpp]view plain

copy

function values(t)  

local i = 0  

return

function () i = i + 1 

return

t[i] end  

end  

t =   

iter = values(t)  

while

true

dolocal element = iter()  

ifelement == nil then 

break

end  

print(element)  

end  

總結下lua閉包,關鍵點是upvalue,然後注意下如何申明乙個揹包,函式(a)裡面返回的是函式(b),b引用了a的區域性變數。

Lua基礎系列 閉包

歡迎來到lua高階系列的部落格 簡單來說就是 對於乙個函式,能夠訪問到外部函式的非全域性變數的一種機制。說起來很繞,我們看乙個栗子 function func1 local x 1 定義乙個內部函式 function func2 print x end 執行這個內部函式 這個例子就是在外部呼叫了fu...

JS閉包解析

js作用域傳送門 function f var fn f var a 200 fn 輸出100呼叫fn函式,輸出a的值,fn中並沒有定義a,所以會向上找a,在f函式的作用域中,有a,值為100。所以就會輸出100,並不會輸出200。全域性作用域中的a和f函式作用域中的a並不相同。這也體現出了閉包的乙...

Lua的upvalue和閉包

lua函式 可以被當成引數傳遞,也可以被當成結果返回 在函式體中仍然可以定義內嵌函式。lua閉包是lua函式生成的資料物件。每個閉包可以有乙個upvalue值,或者多個閉包共享乙個upvalue數值。如果函式f2定義在函式f1中,那麼f2為f1的內嵌函式,f1為f2的外包函式,外包和內嵌都具有傳遞性...