当前位置:   article > 正文

Lua中几种for循环的简单比较_lua for循环

lua for循环

Lua的for循环分为两种:

  • 数值for循环(numeric for)
  • 泛型for循环(generic for)

数值for循环

语义

数值for循环的语法如下:

for i = _initialValue, _limitValue, _stepValue do 
	<body>
end
  • 1
  • 2
  • 3

_initialValue, _limitValue, _stepValue三者都必须是number类型。其中_stepValue为1时可以省略。注意这三者都是在循环开始前一次性求值的。

进入代码时,_initialValue作为初值赋给变量 i,然后与_limitValue值进行比较,如果不大于_limitValue,则执行循环体,否则退出循环。循环体执行结束,根据_stepValue值对变量 i 进行改变,再与_limitValue比较,以此循环。

关于limitValue

数值for循环很简单,没什么可说的,这里面唯一可能需要注意的,其实也并不是循环本身,而是_limitValue的取值。在实际的使用中,一般有如下四种形式:

  • _limitValue的取值为明确的数值,无论是number字面量、number变量还是可计算得到number的表达式都可以
  • _limitValue为math.huge,此时不会给循环设限,但需要在循环体内提供退出循环的途径,避免死循环
  • _limitValue为通过‘#’取得的_table长度,此时,_limitValue的值为_table中数组部分的长度(至于数组长度是怎样的,涉及到Lua的rehash过程,感兴趣的老板可以参照Lua源码:向table内新增元素及rehash过程这篇文章)
  • _limitValue为table.maxn(_table)取得的长度,此时,_limitValue的值为_table中最大的不为nil的数值key的值

在这里插入图片描述
在这里插入图片描述

泛型for循环

语义

泛型for循环的语法如下:

for <varList> in <expList> do
	<body>
end
  • 1
  • 2
  • 3

其中,varList是变量列表,expList是表达式列表。也就是说,在泛型for循环中,变量和表达式都可以为多个。

泛型for的运行机制为:
通过对表达式列表 expList 求值,获取for所需要的三个元素:迭代函数Func、恒定状态Stay,以及控制变量初值Var,即:

local Func, Stay, Var = <expList>
  • 1

其中恒定状态为一个不会随着迭代进行而改变的值,而控制变量则会每次变化,恒定状态和控制变量的初值并不是必需的。

然后调用迭代函数,将恒定状态和控制变量传入迭代函数,并将结果赋值给变量列表 varList,即:

<varList> = Func(Stay, Var)
  • 1

而在变量列表 varList 中,第一个值被称为控制变量,也就是说,实际上每次迭代时传给迭代函数Func的 Var 其实就是 varList 中的第一个值,只不过在首次迭代时需要 expList 来提供一个Var的初值罢了。之后在每次迭代结束时,Func的第一个返回值又会赋值给 varList 的第一个值,于是在下一次迭代时,传给Var的值就发生了变化,从而实现迭代。

当 Var 为 nil 时,也就是某次迭代返回的第一个值为 nil 时,迭代结束,退出循环。

以我们常用的k,v遍历举例

for k,v in pairs(_tab) do
	<body>
end
  • 1
  • 2
  • 3

此时,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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

可见,在进入循环时,ipairs 工厂完整的返回了迭代函数 iterFunc、恒定状态 _table、控制变量初值0。这三者都会被泛型for保存,而每次调用迭代函数 iterFunc 时都是干净的调用其本身,并将泛型for保存的恒定状态和控制变量作为参数传给迭代函数,因此迭代函数不需要借助任何外部的UpValue来保存信息,所以称为无状态迭代。

上文提到的 pairs(_table) 工厂也是无状态迭代,只不过它使用Lua自带的 next 方法作为迭代函数。其内容如下:

function pairs(_table)
	return next, _table, nil
end
  • 1
  • 2
  • 3

当然,也可以不通过工厂,而直接在泛型for中使用迭代函数,比如下面的栗子:
在这里插入图片描述

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/335787
推荐阅读
相关标签
  

闽ICP备14008679号