深入解讀Lua中迭代器與泛型for的使用
泛型for原理
迭代器是一種可以遍歷集合中所有元素的機(jī)制,在Lua中通常將迭代器表示為函數(shù),每調(diào)用一次函數(shù),就返回集合中“下一個(gè)”元素。每個(gè)迭代器都需要在每次成功調(diào)用之間保持一些狀態(tài),這樣才能知道它所在的位置及如何步進(jìn)到下一個(gè)位置,closure就可以完成此項(xiàng)工作。下面的示例是列表的一個(gè)簡(jiǎn)單的迭代器:
function values(t) local i = 0 return function() i = i + 1; return t[i] end end
循環(huán)調(diào)用:
t = {10, 20, 30} iter = values(t) while true do local el = iter() if el == nil then break end print(el) end
泛型for調(diào)用
for el in values(t) do print(el) end
泛型for為一次迭代循環(huán)做了所有的簿記工作。它在內(nèi)部保存了迭代器函數(shù),并在每次迭代時(shí)調(diào)用迭代器,在迭代器返回nil時(shí)結(jié)束循環(huán)。實(shí)際上泛型for保存了3個(gè)值:迭代器函數(shù)f、恒定狀態(tài)s、控制變量a。for做的第一件事就是對(duì)in后面的表達(dá)式求值,并返回3個(gè)值供for保存;接著for會(huì)以s和a來(lái)調(diào)用f。在循環(huán)過(guò)程中控制變量的值依次為a1 = f(s, a0),a2 = f(s, a1),依次類(lèi)推,直至ai為nil結(jié)束循環(huán)。
先看一段代碼
for element in list_iter(t) do print(element) end
在不往下看之前,我們可以先試圖根據(jù)我們已有的知識(shí)結(jié)構(gòu)去理解這段代碼。如果這樣,list_iter(t)應(yīng)該返回一個(gè)類(lèi)似集合的東西,而我們已經(jīng)知道實(shí)際上只返回了一個(gè)匿名函數(shù),也就是迭代器。當(dāng)然,每次調(diào)用迭代器都可以得到一個(gè)元素,迭代器的所有的結(jié)果倒是可以看成一個(gè)集合。因素齊了,我們需要一個(gè)邏輯上的解釋?zhuān)@個(gè)邏輯就是 泛型for的語(yǔ)義。
先看文法規(guī)定:
for <var-list> in <exp-list> do <body> end
整個(gè)過(guò)程是這樣的:
首先,初始化,計(jì)算 in 后面表達(dá)式的值,表達(dá)式應(yīng)該返回 泛型for 需要的三個(gè)值:迭代函數(shù)、狀態(tài)常量、控制變量;與多值賦值一樣,如果表達(dá)式返回的結(jié)果個(gè)數(shù)不足三個(gè)會(huì)自
動(dòng)用nil 補(bǔ)足,多出部分會(huì)被忽略。
第二,將狀態(tài)常量和控制變量作為參數(shù)調(diào)用迭代函數(shù)(注意:對(duì)于 for 結(jié)構(gòu)來(lái)說(shuō),狀態(tài)常量沒(méi)有用處,僅僅在初始化時(shí)獲取他的值并傳遞給迭代函數(shù))。
第三,將迭代函數(shù)返回的值賦給變量列表。
第四,如果返回的第一個(gè)值為 nil 循環(huán)結(jié)束,否則執(zhí)行循環(huán)體。
第五,回到第二步再次調(diào)用迭代函數(shù)。
更具體地說(shuō):
for var_1, ..., var_n in explist do block end
等價(jià)于
do local _f, _s, _var = explist while true do local var_1, ... , var_n = _f(_s, _var) _var = var_1 if _var == nil then break end block end end
泛型 for 在自己內(nèi)部保存三個(gè)值:迭代函數(shù)、狀態(tài)常量、控制變量。
迭代器的狀態(tài)
無(wú)狀態(tài)的迭代器本身不保存任何狀態(tài),for循環(huán)只會(huì)用恒定狀態(tài)和控制變量來(lái)調(diào)用迭代器函數(shù)。這類(lèi)迭代器典型例子就是ipairs,下面是ipairs的Lua實(shí)現(xiàn):
local function iter(s, i) i = i + 1 local v = s[i] if v then return i, v end end function ipairs(s) return iter, s, 0 end
當(dāng)for循環(huán)調(diào)用ipairs(list)時(shí),會(huì)獲得3個(gè)值,然后Lua調(diào)用iter(list, 0)得到list, list[1],調(diào)用iter(list, 1)得到list, list[2],知道得到一個(gè)nil為止。
雖然泛型for只提供一個(gè)恒定狀態(tài)和一個(gè)控制變量用于狀態(tài)的保存,但有時(shí)需要保存許多其他狀態(tài)。這時(shí)可以用closure來(lái)保存,或者將所需的狀態(tài)打包為一個(gè)table,并保存在恒定狀態(tài)中。
閉包、迭代器和泛型for
到現(xiàn)在,Lua為我們準(zhǔn)備了三塊積木:閉包、泛型for和迭代器。一個(gè)循環(huán),我們可以利用閉包+迭代器,也可以使用泛型for+迭代器。那我們?cè)撛趺慈∩崮兀縇ua也給出了建
議。
function iter (a, i) i = i + 1 local v = a[i] if v then return i, v end end function ipairs (a) return iter, a, 0 end for i, v in ipairs(a) do print(i, v) end
這種情況是Lua最推薦的,迭代器不依賴(lài)upvalue,不產(chǎn)生閉包,狀態(tài)常量和控制變量借助泛型for保存,通過(guò)迭代器的參數(shù)傳遞給了迭代器。
再給一個(gè)書(shū)中的例子:
local iterator -- to be defined later function allwords() local state = {line = io.read(), pos = 1} return iterator, state end function iterator (state) while state.line do -- repeat while there are lines -- search for next word local s, e = string.find(state.line, "%w+", state.pos) if s then -- found a word? -- update next position (after this word) state.pos = e + 1 return string.sub(state.line, s, e) else -- word not found state.line = io.read() -- try next line... state.pos = 1 -- ... from first position end end return nil -- no more lines: end loop end
這樣好不好呢,Lua給的答案是否定的。書(shū)中有一段話(huà)說(shuō)得很清楚:
我們應(yīng)該盡可能的寫(xiě)無(wú)狀態(tài)的迭代器,因?yàn)檫@樣循環(huán)的時(shí)候由for 來(lái)保存狀態(tài),不需要?jiǎng)?chuàng)建對(duì)象花費(fèi)的代價(jià)??;如果不能用無(wú)狀態(tài)的迭代器實(shí)現(xiàn),應(yīng)盡可能使用閉包;盡可能不
要使用table 這種方式,因?yàn)閯?chuàng)建閉包的代價(jià)要比創(chuàng)建table 小,另外Lua 處理閉包要比處理table 速度快些。
相關(guān)文章
Lua腳本實(shí)現(xiàn)遞歸刪除一個(gè)文件夾
這篇文章主要介紹了Lua腳本實(shí)現(xiàn)遞歸刪除一個(gè)文件夾,本文給出了C++和Lua兩個(gè)版本的實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-05-05lua閉包的理解以及表與函數(shù)的幾種表達(dá)方法
本文首先通過(guò)具體的例子講解了Lua中閉包的概念,然后總結(jié)了閉包的應(yīng)用場(chǎng)合,最后探討了Lua中閉包的實(shí)現(xiàn)原理,以及l(fā)ua中表與函數(shù)的3種表達(dá)方式的匯總2015-08-08詳解Lua中的變量相關(guān)知識(shí)點(diǎn)
這篇文章主要介紹了Lua中的變量相關(guān)知識(shí)點(diǎn),是Lua入門(mén)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-05-05Lua基礎(chǔ)教程之賦值語(yǔ)句、表達(dá)式、流程控制、函數(shù)學(xué)習(xí)筆記
這篇文章主要介紹了Lua基礎(chǔ)教程之賦值語(yǔ)句、表達(dá)式、流程控制、函數(shù)學(xué)習(xí)筆記,本文來(lái)自個(gè)人總結(jié),需要的朋友可以參考下2014-09-09