在
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的外包函式,外包和內嵌都具有傳遞性...