迭代器與泛型 for 1
迭代器與 closure
- 「迭代器」是一種可以遍歷一種集合中的所有元素的機制
- 在 lua 中通常將迭代器表示為函數(shù)
- 每調(diào)用一次函數(shù),即返回集合中的「下一個」元素
- 每個迭代器都需要在每次成功調(diào)用之間保存一些狀態(tài)
- 這樣就知道它現(xiàn)在所在的位置以及如何步進到一下位置
- 一個
closure
就是一種可以訪問其外部嵌套環(huán)境中的局部變量的函數(shù)
- 這些變量可用于在成功調(diào)用之間保持狀態(tài)值
- 從而使
closure
可以記住它在一次遍歷中所在的位置
- 創(chuàng)建一個
closure
必須創(chuàng)建它的「非局部變量」
- 一個
closure
結(jié)構(gòu)通常包含兩個函數(shù)
-
closure
本身
- 創(chuàng)建該
closure
的工廠函數(shù)
while
-
values
就是工廠薄扁,每次調(diào)用這個工廠率寡,就會創(chuàng)建一個新的 closure
即迭代器其本身
- 這個
closure
將它的想要保存在其外部變量 t
和 i
中
- 每當(dāng)調(diào)用這個迭代器時迫卢,它就從列表 t 中返回下一個值
- 直到最后一個元素返回后,迭代器會返回
nil
冶共,表示迭代的結(jié)束
function values(t)
local i = 0
return function ()
i = i + 1
return t[i]
end
end
t = {123, 333, 444}
iter = values(t) -- 創(chuàng)建迭代器
while true do
local element = iter() -- 調(diào)用迭代器
if element == nil then
break
end
print(element)
end
泛型 for
- 泛型
for
記錄了每一次迭代循環(huán)
- 它在內(nèi)部保存了迭代器函數(shù)乾蛤,因此不需要
iter
變量
- 它在每次新迭代時調(diào)用迭代器每界,并在迭代器返回
nil
時循環(huán)結(jié)束
function values(t)
local i = 0
return function ()
i = i + 1
return t[i]
end
end
t = {123, 333, 444}
-- 泛型 for
t = {123, 333, 444}
for element in values(t) do
print(element)
end
高級用法
- 遍歷當(dāng)前輸入文件中所有單詞的迭代器
- 需要保持的值
- 當(dāng)前行的內(nèi)容
- 以及該行所處的位置
- 使用
string.find
在當(dāng)前行中調(diào)用,以當(dāng)前位置作為起始位置來搜索一個單詞
- 使用模式
%w+
用來表示一個「單詞」家卖, 英語匹配一個或多個文字或數(shù)字字符
- 如果找到了一個單詞眨层,迭代器將當(dāng)前位置更新為該單詞之后的第一個字符,并返回該單詞
- 否則上荡,迭代器讀取新的一行并重復(fù)這個搜索過程
- 若沒有剩余的行趴樱,則返回 nil, 以此表示迭代的結(jié)束。
-- 編寫迭代器
function allwords()
local line = io.read() -- 當(dāng)前行
local pos = 1 -- 一行中的位置
return function () -- 迭代器函數(shù)
while line do -- 只要 line 不為 nil 循環(huán)執(zhí)行
-- 返回開始位置和結(jié)束位置
local start, end = string.find(line, "%w+", pos)
if s then -- 是否找到一個單詞
pos = e + 1 -- 找到一個單詞榛臼,則移到這個單詞的下一個位置
return string.sub(line, s) -- 返回該單詞
else
line = io.read() -- 這一行沒找到伊佃,嘗試讀取下一行
pos = 1
end
end
return nil -- 沒有剩余行了,遍歷結(jié)束
end
end
-- 調(diào)用迭代器
for word in allwords() do
print(word)
end
泛型 for 的語義
- 上述的迭代器需要為每一個新的循環(huán)創(chuàng)建一個新的
closure
沛善,開銷很大
- 泛型
for
在循環(huán)過程內(nèi)保存了迭代器函數(shù)
- 保存了 3 個值
- 一個迭代器函數(shù)
- 一個恒定狀態(tài)
- 一個控制變量
-- var-list 變量列表 , exp-list 表達式列表, 多個元素可用 , 逗號分割
-- 通常表達式列表只有一個元素航揉,即只有一句對迭代器工廠的調(diào)用
for <var-list> in <exp-list> do
<code block>
end
for k, v in pairs(t) do
print(k, v)
end
for line in io.lines() do
io.write(line, "\n")
end
-
變量列表的第一元素稱為「控制變量」,在循環(huán)過程中該值絕不會為
nil
金刁,當(dāng)它為 nil
時循環(huán)結(jié)束
-
for
首先會先對 in
后面的表達式求值帅涂,這些表達式應(yīng)返回 3 個值供 for
保存
- 迭代器函數(shù)
- 恒定狀態(tài)
- 控制變量的初值
- 類似多重賦值,即只有最后一個表達式才會產(chǎn)生多個結(jié)果
- 且只會保留前 3 個值尤蛮,多余的值被丟棄媳友,不足的話,用
nil
補足
- 在初始化步驟后产捞,
for
會以恒定狀態(tài)和控制變量來調(diào)用迭代器函數(shù)
- 然后
for
將迭代器函數(shù)的返回值賦予變量列表中的變量
- 如果變量列表中的第一個元素(即控制變量)的返回值為
nil
醇锚,則循環(huán)終止
- 否則,
for
執(zhí)行循環(huán)體坯临,然后再次調(diào)用迭代器函數(shù)焊唬,并重復(fù)這個過程
- 從
for
構(gòu)建的角度來說,恒定狀態(tài)的內(nèi)容與 for
本身是完全無關(guān)的看靠。for
只是保存了初始化中返回的值赶促,并在調(diào)用迭代器函數(shù)時傳入該值。
for var_1, ..., var_n in <exp-list> do
<code clock>
end
-- 上述代碼等價于如下代碼s
do
-- _f 為迭代器函數(shù)挟炬,_s 為恒定狀態(tài)鸥滨,控制變量的初值為 a0
local _f, _s, _var = <exp-list>
while true do
local var_1, ... var_n = _f(_s, _var)
_var = var_1
if _var == nil then
break
end
end
<code block>
end
-
_f
為迭代器函數(shù),_s
為恒定狀態(tài)谤祖,控制變量的初值為 a?
- 在循環(huán)過程中控制變量的值依次為
a? = f(s, a?)
婿滓、a? = f(s, a?)
以此類推
- 直至
ai
為 nil
結(jié)束循環(huán)
- 如果
for
還有其他變量,那么 他們也會在每次調(diào)用 f
后獲得額外的值