迭代器与泛型 for 1
迭代器与 closure
- 「迭代器」是一种可以遍历一种集合中的所有元素的机制
- 在 lua 中通常将迭代器表示为函数
- 每调用一次函数,即返回集合中的「下一个」元素
- 每个迭代器都需要在每次成功调用之间保存一些状态
- 这样就知道它现在所在的位置以及如何步进到一下位置
- 一个
closure 就是一种可以访问其外部嵌套环境中的局部变量的函数
- 这些变量可用于在成功调用之间保持状态值
- 从而使
closure 可以记住它在一次遍历中所在的位置
- 创建一个
closure 必须创建它的「非局部变量」
- 一个
closure 结构通常包含两个函数
closure 本身
- 创建该
closure 的工厂函数
while
values 就是工厂,每次调用这个工厂,就会创建一个新的 closure 即迭代器其本身
- 这个
closure 将它的想要保存在其外部变量 t 和 i 中
- 每当调用这个迭代器时,它就从列表 t 中返回下一个值
- 直到最后一个元素返回后,迭代器会返回
nil ,表示迭代的结束
function values(t)
local i = 0
return function ()
i = i + 1
return t[i]
end
end
t = {123, 333, 444}
iter = values(t) -- 创建迭代器
while true do
local element = iter() -- 调用迭代器
if element == nil then
break
end
print(element)
end
泛型 for
- 泛型
for 记录了每一次迭代循环
- 它在内部保存了迭代器函数,因此不需要
iter 变量
- 它在每次新迭代时调用迭代器,并在迭代器返回
nil 时循环结束
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
高级用法
- 遍历当前输入文件中所有单词的迭代器
- 需要保持的值
- 当前行的内容
- 以及该行所处的位置
- 使用
string.find 在当前行中调用,以当前位置作为起始位置来搜索一个单词
- 使用模式
%w+ 用来表示一个「单词」, 英语匹配一个或多个文字或数字字符
- 如果找到了一个单词,迭代器将当前位置更新为该单词之后的第一个字符,并返回该单词
- 否则,迭代器读取新的一行并重复这个搜索过程
- 若没有剩余的行,则返回 nil, 以此表示迭代的结束。
-- 编写迭代器
function allwords()
local line = io.read() -- 当前行
local pos = 1 -- 一行中的位置
return function () -- 迭代器函数
while line do -- 只要 line 不为 nil 循环执行
-- 返回开始位置和结束位置
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 -- 没有剩余行了,遍历结束
end
end
-- 调用迭代器
for word in allwords() do
print(word)
end
泛型 for 的语义
- 上述的迭代器需要为每一个新的循环创建一个新的
closure ,开销很大
- 泛型
for 在循环过程内保存了迭代器函数
- 保存了 3 个值
- 一个迭代器函数
- 一个恒定状态
- 一个控制变量
-- var-list 变量列表 , exp-list 表达式列表, 多个元素可用 , 逗号分割
-- 通常表达式列表只有一个元素,即只有一句对迭代器工厂的调用
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
- 变量列表的第一元素称为「控制变量」,在循环过程中该值绝不会为
nil ,当它为 nil 时循环结束
for 首先会先对 in 后面的表达式求值,这些表达式应返回 3 个值供 for 保存
- 迭代器函数
- 恒定状态
- 控制变量的初值
- 类似多重赋值,即只有最后一个表达式才会产生多个结果
- 且只会保留前 3 个值,多余的值被丢弃,不足的话,用
nil 补足
- 在初始化步骤后,
for 会以恒定状态和控制变量来调用迭代器函数
- 然后
for 将迭代器函数的返回值赋予变量列表中的变量
- 如果变量列表中的第一个元素(即控制变量)的返回值为
nil ,则循环终止
- 否则,
for 执行循环体,然后再次调用迭代器函数,并重复这个过程
- 从
for 构建的角度来说,恒定状态的内容与 for 本身是完全无关的。for 只是保存了初始化中返回的值,并在调用迭代器函数时传入该值。
for var_1, ..., var_n in <exp-list> do
<code clock>
end
-- 上述代码等价于如下代码s
do
-- _f 为迭代器函数,_s 为恒定状态,控制变量的初值为 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 为迭代器函数,_s 为恒定状态,控制变量的初值为 a₀
- 在循环过程中控制变量的值依次为
a₁ = f(s, a₀) 、a₂ = f(s, a₁) 以此类推
- 直至
ai 为 nil 结束循环
- 如果
for 还有其他变量,那么 他们也会在每次调用 f 后获得额外的值
|