迭代器
Apr 02, 2022迭代器
剖析 Lua 中 for 迭代器的工作原理,以及重写 Lua 迭代器。
使用 Lua 时最常用的就是 ipairs 和 pairs 了,一个是数组类型的遍历,一种是字典类型遍历(也称泛型遍历)。
由简单的说起
以 ipairs 为例,当我们使用如下结构是
for k, v in ipairs(tab) do
func()
end
-- 其实等同于
for var_1, var_2 in explist do
func
end
-- 再等共同于
local iteraFunc, tab, var = explist
while true do
local var_1, ..., var_n = iteraFunc(tab, var)
if var_1 then
var = var_1
func
end
end
由上可以得出
- 关键字 in 后的表达式是主要用于获取迭代函数的。但是其实也包括一些其他的参数,这得看 for 后面我们添加了多少参数。并且以 ipairs 为例的话,tab 以及 var,应该就是相应的表,var 则是起始索引。explist 只被调用一次,而后每次调用返回的迭代函数,传入不变的 tab,以及每次迭代就变化的 var - 索引
实际使用
local function iterator(tab, index)
print("iterating")
index = index + 1
if tab[index] then
return index, tab[index]
end
end
local function ipairs(tab)
print("get ready")
return iterator, tab, 0
end
for k, v in ipairs(tab) do
func()
end
这样就实现了 ipairs 迭代器
还有种写法,效果是一致
for k, v in func(), tab, ... do
otherFunc()
end
根据原理,也可以写成一个闭包函数,函数中自己保存 tab 和 index,这样可以应对更多复杂的情况,如下
local function GetIterator(tab)
local index = 0
print("get ready")
return function()
print("iterating")
index = index + 1
if tab[index] then
return index, tab[index]
end
end
end
for k, v in GetIterator(tab) do
otherFunc()
end
对于 pairs 的实现则要用 lua 内部提供的 next 函数
local function iterator(tab, index)
local nextIndex, nextValue = next(tab, index)
if nextIndex then
return nextIndex, nextValue
end
end
特殊写法
对于非闭包的迭代,in 关键字有另一种写法
for k, v in func(v1, v2) do
end
-- 等同于
for k, v in func(), v1, v2 do
end
但对于返回闭包函数的迭代就无法使用这种写法,至于为啥,尚不清楚
通过元表实现
由上可知,for 循环实际就是需要在in后面实现一个返回迭代表的函数。
对于实现过自定义过 __index 或 __newindex 的表,再次遍历要是都使用特定的函数会有点麻烦。还有另一种方法,元表种提供一个 __pairs 的键,当使用默认的 pairs 函数进行遍历,会优先调用元表的 __pairs,所以还有这种写法
local mt = {
__pairs = function(tab)
local function iterator(tab, index)
index = index + 1
if tab[index] then
return index, tab[index]
end
end
return iterator, tab, 0
end
}
Comments