迭代器与泛型 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)
泛型 for
- 泛型
for
记录了每一次迭代循环
- 它在内部保存了迭代器函数,因此不需要
iter
变量
- 它在每次新迭代时调用迭代器,并在迭代器返回
nil
时循环结束
function values(t)
local i = 0
return function ()
i = i + 1
return t[i]
end
end
t = {123, 333, 444}
高级用法
- 遍历当前输入文件中所有单词的迭代器
- 需要保持的值
- 当前行的内容
- 以及该行所处的位置
- 使用
string.find
在当前行中调用,以当前位置作为起始位置来搜索一个单词
- 使用模式
%w+
用来表示一个「单词」, 英语匹配一个或多个文字或数字字符
- 如果找到了一个单词,迭代器将当前位置更新为该单词之后的第一个字符,并返回该单词
- 否则,迭代器读取新的一行并重复这个搜索过程
- 若没有剩余的行,则返回 nil, 以此表示迭代的结束。
泛型 for 的语义
- 上述的迭代器需要为每一个新的循环创建一个新的
closure
,开销很大
- 泛型
for
在循环过程内保存了迭代器函数
- 保存了 3 个值
- 一个迭代器函数
- 一个恒定状态
- 一个控制变量
- 变量列表的第一元素称为「控制变量」,在循环过程中该值绝不会为
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
_f
为迭代器函数,_s
为恒定状态,控制变量的初值为 a?
- 在循环过程中控制变量的值依次为
a? = f(s, a?)
、a? = f(s, a?)
以此类推
- 直至
ai
为 nil
结束循环
- 如果
for
还有其他变量,那么 他们也会在每次调用 f
后获得额外的值