迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址
在Lua中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。
泛型 for 迭代器
泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。
泛型 for 迭代器提供了集合的 key/value 对,语法格式如下:
for k, v in pairs(t) do
print(k, v)
end
上面代码中,k, v为变量列表;pair(t)为表达式列表。
demo:
array = {"Lua", "C++"}
for key,value in ipairs(array)
do
print(key, value)
end
console:
1 Lua
2 C++
array = {"Lua", "C++",a=1,b=2,"java"}
for key,value in ipairs(array)
do
print(key, value)
end
console:
1 Lua
2 C++
3 java
再使用pairs对其进行遍历:
以上实例中我们使用了 Lua 默认提供的迭代函数 ipairs。
补充例子01:
local tabFiles = {
[3] = "test2",
[6] = "test3",
[4] = "test1"
}
for k, v in ipairs(tabFiles) do
print(k, v)
end
--它在 ipairs(tabFiles) 遍历中,当key=1时候value就是nil,所以直接跳--出循环不输出任何值。
补充例子02
a={"Hello","World";a=1,b=2,z=3,x=10,y=20;"Good","Bye"}
for i, v in ipairs(a) do
print(v)
end
console:
Hello
World
Good
Bye
从打印出的结果可以看出来,我们的 ipairs并不会输出table中存储的键值对,会跳过键值对,然后按顺序输出table中的值。
也就是说ipairs只是按照索引值顺序,打印出了table中有索引值的数据,没有索引值的不管。
补充例子03:
我们使用pairs,就会从头遍历。
a={"Hello","World";a=1,b=2,z=3,x=10,y=20;"Good","Bye"}
for i, v in pairs(a) do
print(v)
end
console:
Hello
World
Good
Bye
1
10
2
20
3
小结:
所以ipair索引key从整数1开始,而pair则不限制索引key
在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。
* 这里面其实迭代器帮我们保存了一个函数,才实现了这样的遍历*
function values(t)
local i = 0
return function()
i = i + 1
return t[i]
end
end
t = {10, 20, 30}
it = values(t)
while true do
local element = it() --这个函数的生命周期可以理解为在while函数内
if element == nil then
break
end
print(element)
end
--另外一种基于foreach的调用方式(泛型for)
t2 = {15, 25, 35}
for element in values(t2) do --对于for来说其实跟wile可以同样的理解
print(element) --循环终止的条件就是element 为nil
end -- element 等于 values(t2)的返回值
console:
10
20
30
15
25
35
下面我们看看范性for的执行过程:
- 初始化,计算in后面表达式的值,表达式应该返回范性for需要的三个值:迭代函数、状态常量、控制变量;(迭代函数这里面可以理解为values,状态常量就是t2数组,控制变量就是 element)
与多值赋值一样,如果表达式返回的结果个数不足三个会自动用nil补足,多出部分会被忽略。 - 将状态常量和控制变量作为参数调用迭代函数(注意:对于for结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)
- 将迭代函数返回的值赋给变量列表。
- 第四,如果返回的第一个值为nil循环结束,否则执行循环体。
- 第五,回到第二步再次调用迭代函数
根据Lua 的迭代器
包含以下两种类型:
无状态的迭代器
多状态的迭代器
无状态的迭代器
无状态的迭代器是指不保留任何状态的迭代器,因此在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价。
每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。
这种无状态迭代器的典型的简单的例子是ipairs,他遍历数组的每一个元素。
以下实例我们使用了一个简单的函数来实现迭代器,实现 数字 n 的平方:
function square(iteratorMaxCount,currentNumber) --iteratorMaxCount,currentNumber
if currentNumber<iteratorMaxCount --只创建一次
then
currentNumber = currentNumber+1
return currentNumber, currentNumber*currentNumber
end
end
for i,n in square,3,0
do
print(i,n)
end
console:
1 1
2 4
3 9
迭代的状态包括被遍历的表(循环过程中不会改变的状态常量)和当前的索引下标(控制变量),ipairs和迭代函数都很简单,我们在Lua中可以这样实现:
function iter (a, i)
i = i + 1
local v = a[i]
if v then
return i, v
end
endfunction ipairs (a)
return iter, a, 0
end
当Lua调用ipairs(a)开始循环时,他获取三个值:迭代函数iter、状态常量a、控制变量初始值0;然后Lua调用iter(a,0)返回1,a[1](除非a[1]=nil);第二次迭代调用iter(a,1)返回2,a[2]……直到第一个nil元素。
多状态的迭代器
很多情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到table内,将table作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在table内,所以迭代函数通常不需要第二个参数。
以下实例我们创建了自己的迭代器:
array = {“Lua”, “Tutorial”}
function elementIterator (collection)
local index = 0
local count = #collection
– 闭包函数
return function ()
index = index + 1
if index <= count
then
– 返回迭代器的当前元素
return collection[index]
end
end
endfor element in elementIterator(array) –
do
print(element)
end
以上实例输出结果为:Lua
Tutorial
以上实例中我们可以看到,elementIterator 内使用了闭包函数,实现计算集合大小并输出各个元素。