Lua-迭代器的探究

前言

先代表,我是个正在学习 Lua 的新手,在遇到些学习上的问题,通过思考尝试,让后写下我对相关问题知识的认识。
个人博客:https://crabsaberv.github.io/html

菜鸟教程上学习 Lua 迭代器过程当中,遇到了些障碍。虽然根据菜鸟教程中的解释:git

下面咱们看看泛型 for 的执行过程:github

  • 首先,初始化,计算 in 后面表达式的值,表达式应该返回泛型 for 须要的三个值:迭代函数、状态常量、控制变量;与多值赋值同样,若是表达式返回的结果个数不足三个会自动用 nil 补足,多出部分会被忽略。
  • 第二,将状态常量和控制变量做为参数调用迭代函数(注意:对于 for 结构来讲,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
  • 第三,将迭代函数返回的值赋给变量列表。
  • 第四,若是返回的第一个值为nil循环结束,不然执行循环体。
  • 第五,回到第二步再次调用迭代函数

可是在本身更具理解,试着编写一些测试 Lua 无状态迭代器代码时,出现的结果却让人捉急。
可先阅览 Lua 官方文档中对迭代器的描述: Lua 5.3 文档 -3.3.5
(能看懂官方解释,基本上本文就没必要往下看了)shell

经历

初次编写

开始根据菜鸟教程编写了一个无状态迭代器,固然运行结果也正常。闭包

function square(iteratorMaxCount,currerntNumber)
	if currerntNumber < iteratorMaxCount then
		currerntNumber = currerntNumber+1;
		return currerntNumber,currerntNumber*currerntNumber;
	end
end

for i,n in square,10,0
do
	print(i,n)
end

这段代码的行为其实很简单,就是打印了10之内全部的平方数。
根据菜鸟教程描述的 for 的执行过程可知,suqare 就是迭代器函数,10是状态常量,0则是控制变量。而当运行时,会先根据 square 以及参数,获得迭代器的三个元素(迭代函数,状态常量,控制变量),之后就会利用这三个元素来进行迭代的过程。函数

但这里菜鸟教程对这里有些轻描淡写,并没说清楚真正各个参数是如何被使用,尤为是 square 的参数和返回值之间的联系或者是否有联系,却没讲清楚。学习

问题的出现

后来,我在想常常性在 for 中用两个变量来接收,能不能只用一个 临时变量来接收迭代结果,而且迭代器函数可否只有一个参数,一个返回值。测试

因而有了下面的代码:ui

function aa(a)
	if a<10 then
		a = a+1
		return a
	end
end

for i in aa,1 do
	print(i)
end

以我最初对迭代器的理解,应该会执行10次运算,然而:lua

2
2
2
.
.
.

无限循环打印 2 。

此时就开始纳闷了,这个迭代器的内部原理到底是什么,aa() 被作了,其中的参数 a 又作了什么处理,返回的 a 除了赋值给了 i,而后又去了哪里。

为了解决这些疑问,我尝试了不少种不一样的组合形式,好比将参数设置为2个,返回值设置成多个,参数调换位置等,但不少时候最后的表现结果却和预期差的很远。

没有办法,百度不给力,那就只能去看 Lua 的官方文档了。

果不其然,官方的解释很是好理解,还给了示例。

官方解释

参照官方文档-3.3.5的解释:

for statement like

for var_1, ···, var_n in explist do block end

is equivalent to the code:

do
   local f, s, var = explist
   while true do
     local var_1, ···, var_n = f(s, var)
     var = var_1
     if var == nil then break end
     block
   end
 end

上面的迭代器遍历能够看做是下面的过程:

可见一开始由 do 语句块将包裹起来,其中会建立三个变量,而这三个变量 f,s,var 正好对应着迭代器三元素:f-迭代器函数,s-状态常量,var-控制变量。其值由 explist 的返回值得到。这也就证实了为什么 for 迭代器 in 后面的最终函数必须以:“迭代器函数,状态常量,控制变量“ 的形式/结构来编写。

后来在语句块中编写一个 while 循环,其内部又用多个局部变量 var_x,来接收 f(s,var) 的结果。而能看出这些 var_x 就是咱们 for 循环中用于接收迭代过程结果的变量。这就构成了一个闭包,f 就是这里的一个闭包函数。

而下面的语句,将 var_1 接收到的 var 的值又再次赋值给 var。var 是一个外部变量,那么在下次循环中,f(s,var) 的 var 传入的就是新值,也便是控制变量的更新。

当 var 为 nil 时,循环就会跳出。

思考后的结论

注意事项

参考了这些,我认为对于无状态迭代器的编写须要遵照几个规则

  1. for 迭代中,in 后面必须最终能接收到:迭代器函数,状态常量,控制变量 ;且顺序不能改变。
  2. 咱们自行建立的迭代器方法要保证能正常迭代,必须包含两个(或至少两个)参数;同时参数分别表示:"状态常量,控制变量",顺序也不容许改变。
  3. 咱们自建的迭代器方法的返回值的第一个值,会做为下一次迭代的控制变量参数传递进来。而其余参数只会用作返回赋值给 for 的接收变量。

认识

参照这几个规则,基本上能保证咱们编写的无状态迭代器不会出现什么超出预期的问题。

而根据分析,也能得出关于迭代器的内部参数特色:

  • for 迭代 in 的后面能够是个包装器,方便咱们阅读,其返回值就是迭代器函数,状态常量,控制变量 这三个元素。
  • 内部实际上将迭代函数以闭包形式调用。
  • 可见状态常量只有一开始初始化迭代器三元素的过程当中被使用,以后的时候都是不变的,且直接做为对于参数被传递给迭代器函数。
  • 迭代器函数的返回值有可能多也可能少,总之在填补的时候,没有的就会填补 nil ,多余的就被忽视。
  • 迭代器函数的第一个返回值,必须是表示控制变量的值。
  • 要使得迭代函数的迭代过程,只须要使得返回的控制变量的值为 nil 便可。
  • 最后根据官方描述-3.3.5,for 中可使用 break 中断结束循环迭代。
相关文章
相关标签/搜索