Lua迭代器與closure

預(yù)備知識(shí)
lua函數(shù)是一種First-Class Value,即它與傳統(tǒng)的變量并沒有什么區(qū)別。
它可以存儲(chǔ)到變量中、存儲(chǔ)到table中、作為實(shí)參傳遞給其他函數(shù)、作為其他函數(shù)的返回值等。
它們還具有特定的詞法域,也就是說,一個(gè)函數(shù)可以嵌套在另一個(gè)函數(shù)中,內(nèi)部函數(shù)可以訪問外部函數(shù)的變量。

1. closure(閉合函數(shù))

1.1 closure的組成

一個(gè)closure就是一個(gè)函數(shù)加上這個(gè)函數(shù)所需訪問的所有"非局部變量",非局部變量也就是外部函數(shù)的變量

function newCounter()
   local i = 0
        func = function()
                i = i + 1
                return i
        end
   return func
end
c1 = newCounter()
print(c1())
print(c1())

當(dāng)我們每次再調(diào)用newCounter這個(gè)函數(shù)的時(shí)候,也會(huì)得到一個(gè)新的closure,這往往是性能損耗的關(guān)鍵。

1.2 upValue

每個(gè)閉包都可以有一個(gè)upValue值,多個(gè)閉包可以共享一個(gè)upValue數(shù)值
外部函數(shù)的局部變量,因?yàn)樗呀?jīng)是內(nèi)部函數(shù)的upValue,所以局部變量的生命期延長(zhǎng)。當(dāng)我們不再調(diào)用函數(shù),那么閉包就不會(huì)再生成,那么此時(shí)局部變量的生命期就到了盡頭,等待gc回收。

upValue其實(shí)就是局部變量,當(dāng)它還沒有離開它的作用域,它就一直生存在堆棧上,閉包通過指向堆棧的引用了來訪問它們,一旦upValue即將離開自己的作用域,在從堆棧消除之前,閉包就會(huì)為它分配空間并保留當(dāng)前的值。

1.3閉包生成流程

每當(dāng)Lua執(zhí)行一個(gè)函數(shù)時(shí),它就會(huì)創(chuàng)建一個(gè)新的數(shù)據(jù)對(duì)象,這個(gè)數(shù)據(jù)對(duì)象包含了相應(yīng)函數(shù)原型的引用、環(huán)境的引用以及一個(gè)由所有upValue引用組成的數(shù)組,這個(gè)數(shù)據(jù)對(duì)象就稱為閉包。

2. 迭代器

2.1 為什么需要迭代器

編程中我們往往碰到各種各樣的容器,不一樣的容器它們的底層代碼實(shí)現(xiàn)是不同的,那就意味著,遍歷它們需要不同的方式。這樣一來非常不利于代碼重用,所以迭代器就出現(xiàn)了,我們將遍歷容器的操作都封裝在迭代器里,那么我們就不需要考慮這個(gè)容器需要用哪一種遍歷方式。

2.2 什么是迭代器

它是一種可以遍歷一種集合所有元素的機(jī)制。Lua中迭代器常意味著函數(shù),每調(diào)用一次函數(shù),即返回集合的下一個(gè)元素。

2.3 迭代器的使用

每個(gè)迭代器都需要在每次成功調(diào)用之前保持一些狀態(tài),這樣才知道它的位置以及如何進(jìn)入下一步位置。

2.4 用closure方式保持狀態(tài)

一個(gè)closure就是一種可以訪問外部嵌套環(huán)境的局部變量的函數(shù),這些變量可以用在成功調(diào)用之后保持狀態(tài)值,從而closure可以記住它在一次遍歷后的位置。一個(gè)closure結(jié)構(gòu)通常涉及兩個(gè)函數(shù),closure自身和用于創(chuàng)建該closure的工廠函數(shù)

下圖的values就是一個(gè)工廠函數(shù),當(dāng)我們每次調(diào)用它的時(shí)候都會(huì)新建一個(gè)closure,這個(gè)closure就會(huì)將它的狀態(tài)保存到外部變量i和t中,此時(shí)i和t因?yàn)閏losure,生命期延長(zhǎng),在函數(shù)結(jié)束后不會(huì)立馬消失。所以每當(dāng)調(diào)用values時(shí),都會(huì)從列表t返回下一個(gè)值,直到最后一個(gè)元素返回后,迭代器返回nil,至此迭代結(jié)束。


closure
2.5 用泛型for保持狀態(tài)

它和上面的closure相比,效率提高了許多,因?yàn)椴恍枰恳淮握{(diào)用的時(shí)候都去創(chuàng)建一個(gè)新的closure。
泛型for在循環(huán)過程中保存了三個(gè)值:迭代器函數(shù)、恒定變量、控制變量

泛型for

下圖k,v便是變量列表,也就是上圖的var-list。變量列表的第一元素為"控制變量",循環(huán)過程中該值不會(huì)為nil,當(dāng)它為nil時(shí),循環(huán)就結(jié)束了。
2

我們看一下下面的泛型for實(shí)例,ipairs2也就是我們的表達(dá)式的原型,它返回迭代器函數(shù)、恒定對(duì)象、調(diào)用iter函數(shù)為其傳入的初始值。迭代器iter返回值會(huì)賦予變量列表中的變量,如圖為i,v賦給k,v,如果返回值i為nil,那么循環(huán)終止。
泛型for實(shí)例

2.6 無狀態(tài)的迭代器

無狀態(tài)迭代器是不保留任何狀態(tài)的迭代器,避免創(chuàng)建閉包花費(fèi)額外的代價(jià)。其實(shí)上面我們使用的ipairs2就是有一個(gè)無狀態(tài)的迭代器。
下面調(diào)用square的流程就是square(3,0),square(3,1),square(3,2),它將3作為恒定狀態(tài),0作為迭代器函數(shù)的初始值。

function square(iteratorMaxCount,currentNumber)
   if currentNumber<iteratorMaxCount
   then
      currentNumber = currentNumber+1
   return currentNumber, currentNumber*currentNumber
   end
end

for i,n in square,3,0
do
   print(i,n)
end
2.7 多狀態(tài)的迭代器

當(dāng)我們迭代器需要保存多個(gè)狀態(tài)時(shí),泛型for就不適用了,因?yàn)榉盒蚮or只提供一個(gè)恒定狀態(tài)和控制變量用于狀態(tài)的保存。
有兩種方法可以實(shí)現(xiàn)多狀態(tài)迭代器:closure、將所需狀態(tài)打包為一個(gè)table

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容