在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
在lua种,for语句有两种形式
数值型for:基本语法如下
在循环开始之前,每个表达式(exp1,exp2,exp3)都会执行一遍,循环开始后就不再执行。
输出:
var是被for自动声明的局部变量,初始值为exp1,作用范围仅限于for循环内部。var的值小于等于exp2之前的每次循环中都会执行something,每次循环结束后都会将步长(exp3)增加到var上,exp3是可选的,如果没有指定,默认步长为1。如果想跳出循环可以使用break。 如果想用for语句写出一个无限循环,可与参考如下代码:
泛型for:在了解泛型for之前我们必须了解一下lua中的迭代器和闭包 迭代器(iterator):迭代器是一种可以让我们遍历一个集合中所有元素的代码结构。在lua种通常使用函数表示迭代器:每次调用函数的时候,函数会返回集合中的下一个元素。 一个典型的例子就是io.read,每次调用的时候它都会返回标准输入中的下一行,在没有可以读取的行的时候返回nil。 闭包:根据迭代器的概念我们应该能想到,迭代器在连续调用当中应该保存一些状态,这样才能知道当前迭代器的位置以及如何从当前位置进步到下一个位置。对于自定义迭代器而言,闭包为保存状态提供了良好的机制。 闭包就是一个可以访问自身环境中的局部变量的函数。 这些局部变量记录了迭代器连续调用过程中的中间值,让闭包能记住迭代器的位置。当然要创建一个新的闭包,我们还需要非局部变量。因此,一个闭包通常包含两个函数:
以下是一个简单的table迭代器,它遍历table返回所有的值
在这个例子中,values作为工厂,每当它被调用的时候,就会创建一个新的闭包(迭代器本身)。这个闭包将自身的状态保存在外部变量"t"和"i"中,这两个变量是由values封装的。每次调用迭代器,i的值加1,然后返回t[i],i作为迭代器的外部变量,在迭代器返回后不会被清除,其值得以保存下来,起到了记录迭代器位置的作用。在遍历完table的最后一个元素后,迭代器返回nil,表示迭代结束。 我们可以在一个while循环中使用这个迭代器,它展示了迭代器的完整工作流程:
输出:
使用泛型for,我们可以以更简洁的代码实现上述逻辑。
为什么泛型for能实现如此简洁的代码?下面我们就来探究一下泛型for背后做了哪些事情。 泛型for的标准语法如下:
我们把变量列表(var-list)中的第一个变量称为控制变量(control variable),其值永远不会是nil,因为当其值为nil时循环已经结束了。 泛型for要做的第一件事就是对 exp-list 求值,这些表达式应该返回三个值供泛型for保存:
表达式产生的返回值最多保留三个,不足三个则以nil补齐。例如上面的values()工厂只返回了迭代函数,所以不可变状态和控制变量初始值均为nil。用代码表示上述过程就是:
然后泛型for把_s和_v作为参数调用_f,如果返回的第一个值(控制变量)为nil,表示循环结束,否则,把_s和新的返回值作为参数,继续调用_f。其调用过程类似于数列,用伪代码可以表示为:
每次循环产生一次调用。这里_f可以返回多个值,但是只有第一个返回值才是控制变量,用来决定循环是否还要继续。 所以一个形如:
的泛型for语句可以翻译为:
与上面最原始的迭代器相比较,这里的迭代函数_f有一个显著的区别,就是_f可以接收参数了,并且参数的值就包含了当前迭代器的状态,也就是说迭代器自身不需要保存状态了。因此诞生了一个新的概念: 无状态迭代器顾名思义,无状态迭代器就是一种不需要保存任何状态的迭代器。因此在多个循环中可以使用同一个迭代器,从而避免了创建闭包的开销,让代码在性能上得到了提升。ipairs就是一个典型的无状态迭代器。例如:
我们自己可以用lua实现一个ipairs迭代器
输出:
在上述示例中,调用for循环的第一步就是对my_ipairs求值,_f, _s, _v分别获得返回值:
在第一次循环中执行:
执行结果为:
紧接着第二次循环,泛型for会把第一次循环中_f(t, _v)的第一个返回值(i)作为控制变量,进行第二次调用:
执行结果为:
按照此规律进行迭代,直至i的值为nil。 pairs函数也是一个无状态迭代器,它调用的是lua中一个基本的迭代函数:next(t, k)。pairs的原型可以描述如下:
next(t, k)的返回值有两个:
因此我们可以不使用pairs而直接调用next,一样可以实现遍历效果
总结:
|
请发表评论