转载注明出处即可,无需经过本人同意。
本文内容为网上内容总结以及本人的总结,如存在侵权,请告知我本人删除文章。
请在阅读本文之前,先阅读《Lua内容关于for循环的总结(一)》或许对你更有帮助。
经过上文的描述之后,我们知道了使用for遍历的四种基本方法。但是这还不是全部,还有些内容需要我们了解。
看如下这个例子,是否了解:
function square(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
如果上面的例子看不懂,那么请接着往下看。
首先我们需要讨论的是for…in…的语义,即for…in…到底做了什么工作。
for..in的语义
泛型for的语法如下:
for <var-list> in <exp-list> do
<body>
end
其中,< var-list > 是一个或多个变量名的列表,以逗号分隔;< exp-list >是一个或多个表达式的列表,同样以逗号分隔。通常表达式列表只有一个元素,即一句对迭代器函数的调用。例如:
for k, v in pairs(t) do
print(k, v)
end
for做的第一件事就是对in后面的表达式求值,这些表达式应该返回3个值供for保存:迭代器函数、恒定状态和控制变量的初值。这里和多重赋值是一样的,只有最后一个表达式才会产生多个结果,并且只会保留前3个值,多余的值会被丢弃;而不够的话,就以nil补足。如下的闭包函数只返回了迭代器函数,而没有返回恒定状态和控制变量:
```
function list_iter(tab)
local i=0
return function()
i=i+1
return tab[i]
end
end
tab={12,233,1221,231}
for k in list_iter(tab) do
print(k)
end
-----------------------------------------------------
题外话:
什么是多重赋值?(知道的可以跳过)
- 如果函数为表达式最后一个,所有返回值将被返回。
- 如果函数不是最后一个,**只有第一个值**被返回。
```
function doA()
return 11,22
end
--下面注释的这行,doA()不是表达式最后一个,所以只返回一个值
--a,b,c的值分别为11,2,nil。
--a,b,c=doA(),2
a,b,c=2,doA()
print(a,b,c)
-----------------------------------------------------
接着上面的内容继续:
in后面的表达式要返回三个值,下面通过代码来解释一下,比如:
for k,v in <explist> do
<block>
end
上面的代码就等价于以下代码:
do
-- 返回迭代器函数、恒定状态和控制变量的初值
local _f, _s, _var = <explist>
while true do
local k, v = _f(_s, _var)
--迭代器返回的第一个值赋值给_var。
_var = k
if _var == nil then
break
end
print(k .. "," .. v)
end
end
--我们先删掉复杂的部分,代码变成如下:
do
local _f = <explist>
while true do
local k, v = _f()
if k == nil then
break
end
print(k .. "," .. v)
end
end
我们来看看for in真面目的第一句代码:
local _f, _s, _var = < explist >
三个返回值分别代表迭代器函数(_f)、恒定状态(_s)、控制变量初值(_var)。
迭代器函数:就不用解释了,就是我们的闭包函数返回的函数。
恒定状态:其实就是一个变量,这个变量一直不变,所以称之为恒定。
控制变量初值:和恒定相对于的,这是一个会不断改变的变量。
比如我们上面的list_iter函数,它只有一个返回值,就是那个闭合函数,所以,_s和_var都是nil。
接着调用local k, v = _f(_s, _var); 这实际上就是调用了闭合函数,并且将恒定值和变量值都作为参数传递进去。
Lua的函数是很自由的,即使_f函数本身没有参数,也可以传参数进去,不会影响什么,所以,两个nil值传进去了,没有任何事情发生,就像是直接调用_f()一样。
再下一句代码:_var = k; 这是把闭合函数(_f)的第一个返回值保存起来,因为每次调用闭合函数(_f)返回值都是下一个迭代值,所以_var每次都是不一样的值。这里需要注意的是_var的值等于闭合函数的第一个返回值,如果闭合函数只返回v值,那么_var=v;如果闭合函数返回两个值,且第一个值是k,则_var=k。此处只是为了说明_var的值不一定等于k。
如果_var的值为nil,则停止循环,结束迭代。因此,我们编写迭代器的时候,迭代结束的方式就是让第一个返回值为nil。
那么,如果我们让list_iter函数返回恒定状态和控制变量初值,又是什么样的情况呢?
function list_iter(tab)
local i=0
--因为下面的函数返回的第一个值是i,所以var=i
return function(s,var)
i=i+1
if tab[i]==nil then
return nil
end
print("恒定值"..s,"控制变量"..var)
return i,tab[i]
end,10,0 --这里就是返回迭代器函数以及恒定状态和控制变量值。
end
t={"aaaa","aaa","aa"}
for k,v in list_iter(t) do
print(k,v)
end
--返回的内容如下
-- 恒定值10 控制变量0
-- 1 aaaa
-- 恒定值10 控制变量1
-- 2 aaa
-- 恒定值10 控制变量2
-- 3 aa
恒定值自然是一直不变的,而变量值在每一次调用了闭合函数之后,就会赋值为k的值,所以变量值一直按着table的key值在变化(这里的变量值按照table的key值变化,是针对上面的例子的。因为闭包函数返回的第一个值是key)。
博文参考内容:http://blog.csdn.net/musicvs/article/details/40351197
现在回到一开始的问题,是否了解其运行原理:
function square(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
其实上面的内容跟无状态的迭代器有关,可以自行查找相关内容,这里就不多说明。
经过上面一大段的说明,可能会觉得上面例子中的in 后面的就是我们需要求的表达式,那么square的参数在哪里?而且square是放在表达式的前面,那么square 只返回一个值,明显这不是我们需要的。
现在解释原因:
上面的square不是我们需要求的表达式,而是表达式返回的迭代函数。而后面的两个值也就是我们所求的表达式返回的恒定状态以及控制变量。
而在前面的说明for…in…中,恒定状态以及控制变量是需要传入迭代器函数中的,所以上面in 后面的两个值将作为square的参数。这样一来,也就解释清楚了。