通过学习Lua后,你可能会碰到很多种for的循环,其中的差异你是否了解呢?本篇文章将介绍本人目前所认识到的所有for有关内容。你将会了解如下内容:
for循环的分类:数值遍历以及泛型遍历
pairs以及ipairs的区别(这部分内容将会出现在for循环的分类中出现)
泛型遍历的分类:无状态迭代器以及多状态迭代器(现在看不懂没关系,可以先放着,往后看)
for循环的分类可以分为数值遍历以及泛型遍历。
数值遍历是根据表的长度进行遍历,而泛型遍历是通过迭代器遍历。下面分别进行阐述。
如下所示:
tbtest={12,3,12,2312}
--第一种
for i=1, #(tbtest) do
print(tbtest[i])
end
--第二种
for i=1, table.maxn(tbtest) do
print(tbtest[i])
end
第一种是使用 ‘#’,这个符号的作用是是获取table的长度,比如:
tbtest = {
[1] = 1,
[2] = 2,
[6] = 6,
["3"] = 5
}
--输出的结果为2,后面解释。
print(#(tbtest))
#可以获得表的长度。但是要求表的key是数值,且从1开始递增的(这里的递增是指在上一个值的基础上加1)。
从上面的解释就知道了,为什么上面的例子输出的是2。键[“3”]是字符串,而不是数值,所以不能算。而[6]与之前的两个键不是递增的关系,所以也不能算。故此上面的结果为2。
知道了#的使用规则,可以试着修改上面的例子。
例如将key=[1]的键值对删除,结果就为0,因为键key没有从1开始递增。
还有一点需要注意的,#只要求key是递增的,而没有要求key是按照递增的顺序排列。如将上面的代码修改为如下:
tbtest = {
[1] = 1,
[3] = 6,
[4]= 7,
[2] = 2,
}
--输出的结果为4。
print(#(tbtest))
注意上面的key排列顺序,tbtest[3],tbtest[4]的位置放在tbtest[2]的前面,虽然它们的排列顺序没有按照从1递增的顺序排列,但是它们的key是从1开始递增的,也就是说使用 # 对排列顺序没有要求。
—————————————————————-
题外话:
1 . 在这里提一下# 的另一个用法:#还可以获得字符串的长度,如下所示:
name="asd"
print(#name)
2 . table.getn(table)
–等同于操作符 #
作用:得到一个table的大小。
注意:该table的key必须是有序的,索引是从1开始的。
tbtest = {
[1] = 1,
[3] = 6,
[4]= 7,
[2] = 2,
}
--输出4
print(table.getn(tbtest))
—————————————————————-
for i=1, #(tbtest) do这种遍历,只能遍历当tbtest中存在key为1的value时才会出现结果,而且是按照key从1开始依次递增1的顺序来遍历,找到一个递增不是1的时候就结束不再遍历,无论后面是否仍然是顺序的key。
key也要求是数值,而不能是字符串数字。即要求是[1]=10,而不能是[“1”] =10。
第二种是使用table.maxn()。
table.maxn获取的只针对整数的key,字符串的数字key是没办法获取到的。获取的是key值中最大的值。
和table内的定义顺序没有关系,无论你是否先定义的key为6的值,table.maxn都会获取整数型key中的最大值。
此种遍历效率极低,因为如果你整数key中定义的最大的key是10000,然而10000以下的key没有几个,那么这么遍历会浪费很多时间,因为会从1开始直到10000 每一个元素都会查找一遍,实际上大多数元素都是不存在的。比如:
tbtest = {
[1] = 1,
[10000] = 2,
}
--请自行测试,结果就不展示了
for i=1, table.maxn(tbtest) do
print(tbtest[i])
end
此外如果table是:
tbtest = {
["a"] = 1,
["b"] = 2,
["c"] = 3,
}
--那么打印的就全部是0了。
print(table.maxn(tbtest))
print(#(tbtest))
因为table.maxn(),# 都要求key为数值,而不是字符串,即使是数字字符串也不行(即[“1”]=10)
如下所示:
tbtest={12,23,211,23}
--第三种
for key, value in pairs(tbtest) do
print(key,value)
end
--第四种
for key, value in ipairs(tbtest) do
print(key,value)
end
泛型遍历就是使用迭代器来遍历,例如pairs、ipairs,也可以使用自己定义的迭代器(稍后再介绍)。
首先要明确一点,就是lua中table并不是像是C/C++中的数组一样是顺序存储的,准确来说lua中的table更加像是C++中的map,通过Key对应存储Value,但是并非顺序来保存key-value对,而是使用了hash的方式,这样能够更加快速的访问key对应的value。
我们也知道hash表的遍历需要使用所谓的迭代器来进行,同样,lua也有自己的迭代器,也就是paris以及ipairs。
for k,v in pairs(tbtest) do 这样的遍历顺序并非是tbtest中table的排列顺序,而是根据tbtest中key的hash值排列的顺序来遍历的。
如下代码:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
[4] = 4,
}
for key, value in pairs(tbtest) do
print(value)
end
--输出结果为:
--[[
1
2
4
3
]]
从上面可以看到,tbtest[4]的结果在tbtest[3]的上面。也就是说使用pairs()并不是按照key的排列顺序来输出的,而是按照key的hash值排列顺序来遍历。
lua也提供了按照key的大小顺序来遍历的,注意,是大小顺序,仍然不是key定义的顺序,这种遍历方式就是for k,v in ipairs(tbtest) do。
for k,v in ipairs(tbtest) do 这样的循环必须要求tbtest中的key为顺序的,而且必须是从1开始,ipairs只会从1开始按连续的key顺序遍历到key不连续为止。 注意是从1开始的。
(注意是值为顺序的,而不要求定义时也是顺序的。参考如下示例)
--[[
ipairs只能遍历key为数字的,而不能遍历字符串数字。
即key值可以为[3],而不能为["3"]。
而且只能从key为1开始遍历,然后在key中寻找按1递增的key。
这些不需要按照顺序排列,如下所示。
]]
t={"a",12,[5]=12,[4]=22,[3]="c"}
for k,v in ipairs(t) do
print(k,v)
end
--[[输出结果如下:
1 a
2 12
3 c
4 22
5 12
]]
从上面的例子可以看出,只要求key是从1开始递增的,而不要求在排列的顺序上也是按照递增的顺序排列。