0 引言
本文试图用 Lua 实现 Lua 的泛型 for 和 ipairs 迭代器。
笔者在一个令人头秃的夜晚学习《Lua 程序设计(第 2 版)》第 7 章:迭代器与泛型 for,第一遍读下来完全看不懂。网上冲浪之后,发现这篇文章复现了泛型 for 和 ipairs 迭代器的行为。笔者看懂了,并且大受震撼,遂决定自己动手,丰衣足食。
首先看一下 Lua 的泛型 for 配合 ipairs 迭代器的运行情况:
a = {'a', 'b', 'c', nil, 'd'}
b = {nil, 'a', 'b', 'c', nil, 'd'}
print('ipairs(a):')
for i, v in ipairs(a) do
print(i, v)
end
print('ipairs(b):')
for i, v in ipairs(b) do
print(i, v)
end
输出:
ipairs():
1 a
2 b
3 c
ipairs(b):
以上就是我们要复现的功能。复习一下 ipairs 迭代器的功能:从下标 1 开始遍历 table,并且遇到值为 nil 的时候停止迭代。如果一开始就遇到 nil,则一次也不会迭代。
1 实现 ipairs()
首先写一下我们自己的迭代器函数,它有两个参数
- tab:恒定状态,这里就是要遍历的 table
- iterVar:控制变量,这里就是 table 的下标
返回值也有两个,分别是下一轮迭代的索引和值
function myIpairs(tab, iterVar)
-- 第一次调用迭代器的时候是不会有第二个参数的
-- 例如ipairs(a)
-- 即iterVar == nil
if iterVar == nil then
-- 首先检查第一个值是不是nil
if tab[1] == nil then
return nil
end
-- 如果不是nil,返回初始情况
return 1, tab[1]
-- 如果不是第一次迭代
else
-- 同样检查下一个值是不是nil
if tab[iterVar + 1] == nil then
return nil
end
-- 不是的话正常返回下一轮的索引和值
return iterVar + 1, tab[iterVar + 1]
end
end
以上这个只是迭代器函数本身,我们需要一个迭代器生成器,来返回这个迭代器函数,以及恒定状态和控制变量的初始值,下面代码中的 myIpairsFactory
就是这个迭代器生成器。(从 closure 的角度来说,迭代器函数只是一个 closure,迭代器生成器是生成这个 closure 的工厂)
a = {'a', 'b', 'c', nil, 'd'}
b = {nil, 'a', 'b', 'c', nil, 'd'}
print('ipairs(a):')
for i, v in ipairs(a) do
print(i, v)
end
print('ipairs(b):')
for i, v in ipairs(b) do
print(i, v)
end
function myIpairsFactory(tab)
return function(tab, iterVar)
if iterVar == nil then
if tab[1] == nil then
return nil
end
return 1, tab[1]
else
if tab[iterVar + 1] == nil then
return nil
end
return iterVar + 1, tab[iterVar + 1]
end
end, tab, nil
end
print('myIpairsFactory(a):')
for i, v in myIpairsFactory(a) do
print(i, v)
end
print('myIpairsFactory(b):')
for i, v in myIpairsFactory(b) do
print(i, v)
end
我们用以上代码来验证 myIpairsFactory
和 ipairs
的行为是否一致,结果是一致的。输出:
ipairs(a):
1 a
2 b
3 c
ipairs(b):
myIpairsFactory(a):
1 a
2 b
3 c
myIpairsFactory(b):
2 实现泛型 for
根据《Lua 程序设计(第 2 版)》P58 的伪码:
for var_1, ..., var_n in <explist> do <block> end
-- 等价于以下代码
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 i, v in ipairs(b) do
print(i, v)
end
i, v
对应var_1, ..., var_n
ipairs(b)
对应<explist>
print(i, v)
对应<block>
并且代码中:
_f
表示我们的迭代器函数_s
表示恒定状态,在这个例子里就是某个待输出的 table_var
表示控制变量,在这个例子里就是 table 的下标
根据以上伪码,笔者实现了自己的泛型 for:
-- for i, v in iter(tab) do <block> end
function myGenericFor(iter, tab)
local _f, _s, _var = iter(tab)
while true do
local i, v = _f(_s, _var)
_var = i
if _var == nil then break end
--<block>
print(i, v)
end
end
最后用以下代码验证 myGenericFor
是否复现了泛型 for 的功能:
a = {'a', 'b', 'c', nil, 'd'}
b = {nil, 'a', 'b', 'c', nil, 'd'}
function myIpairsFactory(tab)
return function(tab, iterVar)
if iterVar == nil then
if tab[1] == nil then
return nil
end
return 1, tab[1]
else
if tab[iterVar + 1] == nil then
return nil
end
return iterVar + 1, tab[iterVar + 1]
end
end, tab, nil
end
-- for i, v in iter() do <block> end
function myGenericFor(iter, tab)
local _f, _s, _var = iter(tab)
while true do
local i, v = _f(_s, _var)
_var = i
if _var == nil then break end
--<block>
print(i, v)
end
end
print('myGenericFor(ipairs, a):')
myGenericFor(ipairs, a)
print('myGenericFor(myIpairsFactory, a):')
myGenericFor(myIpairsFactory, a)
print('myGenericFor(ipairs, b):')
myGenericFor(ipairs, b)
print('myGenericFor(myIpairsFactory, b):')
myGenericFor(myIpairsFactory, b)
结果是一致的。输出:
myGenericFor(ipairs, a):
1 a
2 b
3 c
myGenericFor(myIpairsFactory, a):
1 a
2 b
3 c
myGenericFor(ipairs, b):
myGenericFor(myIpairsFactory, b):
3 书中代码
更加简洁:
local function iter(a, i)
i = i + 1
local v = a[i]
if v then
return i, v
end
end
function myIpairs(a)
return iter, a, 0
end