赞
踩
Lua的for循环分为两种:
数值for循环的语法如下:
for i = _initialValue, _limitValue, _stepValue do
<body>
end
_initialValue, _limitValue, _stepValue三者都必须是number类型。其中_stepValue为1时可以省略。注意这三者都是在循环开始前一次性求值的。
进入代码时,_initialValue作为初值赋给变量 i,然后与_limitValue值进行比较,如果不大于_limitValue,则执行循环体,否则退出循环。循环体执行结束,根据_stepValue值对变量 i 进行改变,再与_limitValue比较,以此循环。
数值for循环很简单,没什么可说的,这里面唯一可能需要注意的,其实也并不是循环本身,而是_limitValue的取值。在实际的使用中,一般有如下四种形式:
泛型for循环的语法如下:
for <varList> in <expList> do
<body>
end
其中,varList是变量列表,expList是表达式列表。也就是说,在泛型for循环中,变量和表达式都可以为多个。
泛型for的运行机制为:
通过对表达式列表 expList 求值,获取for所需要的三个元素:迭代函数Func、恒定状态Stay,以及控制变量初值Var,即:
local Func, Stay, Var = <expList>
其中恒定状态为一个不会随着迭代进行而改变的值,而控制变量则会每次变化,恒定状态和控制变量的初值并不是必需的。
然后调用迭代函数,将恒定状态和控制变量传入迭代函数,并将结果赋值给变量列表 varList,即:
<varList> = Func(Stay, Var)
而在变量列表 varList 中,第一个值被称为控制变量,也就是说,实际上每次迭代时传给迭代函数Func的 Var 其实就是 varList 中的第一个值,只不过在首次迭代时需要 expList 来提供一个Var的初值罢了。之后在每次迭代结束时,Func的第一个返回值又会赋值给 varList 的第一个值,于是在下一次迭代时,传给Var的值就发生了变化,从而实现迭代。
当 Var 为 nil 时,也就是某次迭代返回的第一个值为 nil 时,迭代结束,退出循环。
以我们常用的k,v遍历举例
for k,v in pairs(_tab) do
<body>
end
此时,pairs内部返回的迭代器函数我们假设为F,F在进行迭代时,返回的第一个值为_tab中的一个键,我们查看Lua源码可以看到NodeKey的结构:
这样通过NodeKey(比如我们称之为N1)的next就可以找到下一个NodeKey(N2),并将其保存给控制变量,再下一次迭代的时候再通过N2的next找到N3 。而当迭代函数F返回的第一个值为空时,表示没有找到next的NodeKey,也就是说没有其他的元素了,于是循环结束(当然,这一部分只是我自己的理解,并没有跟源码,不保证真实的过程是这样的)。
泛型for循环可能出现的问题就是会产生闭包。如下面的栗子:
这种情况下,TestClosure是一个迭代函数的工厂,它在返回迭代函数的时候创建了一个闭包。迭代函数通过UpValue(_tab 和 i)来保存迭代过程中所需要的状态,因而也称为有状态迭代。
由于大量使用有状态迭代时会产生大量的闭包,在讲求效率的场景会有比较大的消耗(主要时UpValue的消耗),而且如果有嵌套的情况也比较容易晕,所以通常不太推荐使用有状态的迭代。
与有状态迭代相对的便是无状态迭代,即迭代函数内不保存任何的状态,而是利用泛型for所保存的恒定状态和控制变量实现迭代,比如常用的 ipairs(_table) 工厂,就是一种无状态迭代,其内容用 Lua 可表示如下:
local function iterFunc(_table, _cnt) _cnt = _cnt + 1 local _value = _table[_cnt] if not _value then return nil end return _cnt, _value end function ipairs(_table) return iterFunc, _table, 0 end local _testTab = {1,2,3,4,5} function TestForFunc() for k,v in ipairs(_testTab) do <body> end end
可见,在进入循环时,ipairs 工厂完整的返回了迭代函数 iterFunc、恒定状态 _table、控制变量初值0。这三者都会被泛型for保存,而每次调用迭代函数 iterFunc 时都是干净的调用其本身,并将泛型for保存的恒定状态和控制变量作为参数传给迭代函数,因此迭代函数不需要借助任何外部的UpValue来保存信息,所以称为无状态迭代。
上文提到的 pairs(_table) 工厂也是无状态迭代,只不过它使用Lua自带的 next 方法作为迭代函数。其内容如下:
function pairs(_table)
return next, _table, nil
end
当然,也可以不通过工厂,而直接在泛型for中使用迭代函数,比如下面的栗子:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。