Lua - 手动实现 ipairs 迭代器和泛型 for

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

我们用以上代码来验证 myIpairsFactoryipairs 的行为是否一致,结果是一致的。输出:

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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值