for循环分为两种:数值型(numerical)for和泛型(generic)for。
1、数值型for
语法如下:
for var = exp1, exp2, exp3 do
something
end
上述循环中,var的作用范围为for循环内部,其值从exp1变化到exp2之前,每次循环都会执行something,并在每次循环结束后将步长(step)exp3增加到var上。第三个参数exp3为可选,不存在则设步长为1。若不想给循环设置上限,可以使用常量math.huge。
2、泛型for
在讲泛型for之前,先回顾一下迭代器和闭包。
迭代器是一种可以让我们遍历一个集合中所有元素的代码结构。在lua中,通常使用函数表示迭代器:每次调用函数时,函数会返回集合中的“下一个”元素。
所有的迭代器都需要在连续的调用之间保存一些状态,这样才能知道当前迭代所处的位置,及如何从当前位置步进到下一位置。而闭包则为保存状态提供了一种良好的机制。注意此处,一个闭包就是一个可以访问其自身环境中一个或多个局部变量的函数。这些变量将连续调用过程中的值并将其保存在闭包中,从而使得闭包记住迭代所处的位置。
泛型for语法:
for var-list in exp-list do
body
end
其中,var-list为变量名列表,exp-list为表达式列表,两者都以逗号隔开。特殊的是,变量列表的第一个变量成为控制变量,其值永远不为nil,因为为nil时循环已经结束了。
泛型for要做的第一件事就是对 exp-list 求值,这些表达式应该返回三个值供泛型for保存:
1、迭代函数
2、不可变状态
3、控制变量初始值
无状态迭代器
顾名思义,无状态迭代器就是一种不需要保存任何状态的迭代器。因此在多个循环中可以使用同一个迭代器,从而避免了创建闭包的开销,让代码在性能上得到了提升。ipairs就是一个典型的无状态迭代器,例如:
t = {"this", "is", "test"}
for k,v in ipairs(t) do
print(k,v)
end
迭代的状态由正在被遍历的表(一个不可变状态,不会在循环中改变)及当前的索引值(控制变量)组成。ipairs(工厂)和迭代器都很简单,我们可以用lua语言编写出来:
local function self_iter(t, i)
-- body
print("execute:"..i)
i = i + 1
local v = t[i]
if v then
return i, v
end
end
function self_ipairs(t)
-- body
print("set up")
return self_iter, t, 0
end
for i, v in self_ipairs({"This", "is", "test", "code"}) do
print(i, v)
end
print("=====================我是分界线==============================")
运行结果:
set up
execute:0
1 This
execute:1
2 is
execute:2
3 test
execute:3
4 code
execute:4
=====================我是分界线==============================
当调用for循环中的self_ipairs(t)时,self_ipairs(t)会返回三个值,即迭代函数self_iter、不可改变状态表t、控制变量的初始值0。
之后,lua语言调用iter(t,0),得到1,t[1](除非t[1]已经变成了nil),第二次迭代,lua语言调用iter(t,1),得到2,t[2],以此类推,直至得到第一个为nil的元素。
pairs函数也是一个无状态迭代器,它调用的是lua中一个基本函数:next(t, k)
function pairs(t)
return next, t, nil
end
k是表t的一个键。next(t, k)的返回值有两个:
1、随机次序返回k的下一个键,当key==nil的时候返回表中的第一个键,当所有表便利完毕,返回nil
2、k对应的值,当k==nil的时候返回表中的第一个值
因此我们可以不使用pairs而直接调用next,一样可以实现遍历效果
for k, v in next, {"This", "is", "test", "code"} do
print(k, v)
end
归纳:
1、lua中,for循环分为数值型和泛型两种。
2、泛型for的实现是利用迭代器,而迭代器的实现是利用闭包
3、迭代器分为有状态的和无状态的,且只有在for循环中才有无状态迭代器。