Lua 中的范型for

Lua有一种很自然的循环方式,即Generic for。它的格式是这样的:

for namelist in iterator do
   block
end

其中iterator是一个迭代器函数,它可以有一个或多个返回值,namelist是逗号分隔的循环变量名列表,用来接收每次调用迭代器函数得到的返回值。这段程序的语义与下面形式的代码相同:

while true do
   local namelist = iterator()
   if nil == first(namelist) then break end
   block
end

first(namelist)表示namelist的第一个变量名。

    Lua已经提供了若干个常用的迭代器生成函数,最常见的就是ipairs和pairs。ipairs(t)以一个table作为参数,生成一个依次返回(1,t[1])、(2,t[2])、(3,t[3])......的迭代器。下面这段代码

t = {"Monday","Tuesday", "Wednesday","Thursday", "Friday","Saturday","Sunday"}

for i,v in ipairs(t) do
   print(i,v)
end

将会打印出

1        Monday
2        Tuesday
3        Wednesday
4        Thursday
5        Friday
6        Saturday
7        Sunday

。pairs同样用一个table作为参数,它生成的迭代器依次返回该表中所有的key和value。

    Generic for搭配ipairs和pairs用起来相当方便,但是它们却有一个很难察觉的缺陷。看看这段代码:

for i,v in ipairs(t) do
  print(i,v)
 
-- some code changes i
  i = 0
end

许多人(包括我)都猜不出它竟然会不停地疯狂打印

1        Monday

,如果用pairs替代ipairs也不会好到哪里去: 在打印出一行数据后便抛出个 invalid key for `next' 错误。

    由此可知,使用了ipairs和pairs的Generic for在语义与上文while true ... end形式的代码并不完全相同,它们生成的迭代器需要使用循环变量,而如果循环体改变了循环变量值的话,那么迭代器就很可能会象在本例中那样找不着北了。因此,《Programming in Lua》的4.3.5中便有这么一段谆谆告诫: “The generic loop shares two properties with the numeric loop: The loop variables are local to the loop body and you should never assign any value to the loop variables. "。

    ipairs和pairs的这个缺陷是由于它们要用当前循环变量的值来作为下一次迭代的起点,那么这是不是必需的呢?当然不是,我们完全可以通过closure来实现一个更安全的ipairs :

function myipairs (t)
   local i = 0
   local n = table.getn(t)
   return function ()
             i = i + 1
             if i <= n then return i,t[i] end
          end
end

t = {"Monday","Tuesday", "Wednesday","Thursday", "Friday","Saturday","Sunday"}

for i,v in myipairs(t) do
  print(i,v)
  -- some code changes i
  i = 0
end

    试试上面这段代码,怎么样?是不是工作得很好?为什么它不会发疯?其实很简单,myipairs生成的迭代器(一个closure)把需要用到的迭代信息保存到它的upvalue(i和n)中,这样即使循环体更改了循环变量也不会影响到迭代状态。感谢closure,我们终于可以放心大胆地粗心一点了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值