【前言】

迭代器就是一种可以遍历一种集合中所有元素的机制,在Lua中,通常将迭代器表示为函数。每调用一次函数,就返回集合中的“下一个”元素。每个迭代器都需要在每次成功调用之后保存一些状态,这样才能知道它所在的位置及如何走到下一个位置,通过之前博文的总结,闭包对于这样的任务提供了极佳的支持。现在我们就用代码来实现一个简单的迭代器。

function values(tb)
local i =
return function ()
i = i +
return tb[i]
end
end local testTb = {, , }
for value in values(testTb) do
print(value)
end

这就是一个最简单的迭代器,使用闭包来完成整个任务;这只是一个简单的例子,接下来,再看看泛型for的语义。

【泛型for的语义】

泛型for比较复杂,它在循环过程内保存了迭代器函数。它实际上保存着3个值:一个迭代器函数、一个恒定状态和一个控制变量。接下来,分别进行总结。
泛型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补足。

在初始化完成以后,for会以恒定状态和控制变量来调用迭代器函数。然后for将迭代器函数的返回值赋予变量列表中的变量。如果第一个返回值为nil,那么循环就终止,否则,for执行它的循环体,随后再次调用迭代器函数,并重复这个过程。在前言部分的代码中,只是返回了迭代器函数,并没有返回恒定状态和控制变量。下面通过代码来说明这个问题,比如:

for var_1, ..., var_n in <explist> do <block> end -- 就等价于以下代码:
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
end

【无状态的迭代器】

所谓“无状态的迭代器”,就是一种自身不保存任何状态的迭代器。因此,我们可以在多个循环中使用同一个无状态的迭代器,避免创建新的闭包的开销。

在每次迭代中,for循环都会用恒定状态和控制变量来调用迭代器函数。一个无状态的迭代器可以根据这两个值来为下次迭代生成下一个元素。这类迭代器的代表就是ipairs。它可以用来迭代一个数组的所有元素。如下述演示代码:

local aTb = {"One", "Two", "Three"}
for i, v in ipairs(aTb) do
print(i, v)
end

在这里,迭代器状态就是需要遍历的table(一个恒定状态,它不会在循环中改变)及当前的索引值(控制变量)。我们可以使用Lua代码来实现ipairs,大概就如下代码:

local function iter(a, i)
i = i +
local v = a[i]
if v then
return i, v
end
end function ipairs(a)
return iter, a,
end

函数pairs与ipairs类似,也是用于遍历一个table中的所有元素。不同的是,它的迭代器函数是Lua中的一个基本函next。

function pairs(a)
return next, t, nil
end

在调用next(t, k)时,k是table t的一个key。此调用会以table中的任意次序返回一组值:此table的下一个key,及这个key所对应的值。而调用next(t, nil)时,返回table的第一组值。若没有下一组值时,next返回nil。所以,我们也可以使用next来判断一个table是否为空。

对于大家经常迷惑的ipairs和pairs的区别,在这里就能看的一清二楚了,ipairs只能用于遍历index是整型的table,同时,由于ipairs返回的控制变量初值为0,这就决定了,ipairs只能访问index从1开始的key和value;ipairs不能返回nil,当key对应的值为nil时,就直接终止遍历;而pairs则没有要求。关于ipairs和pairs的具体差异,请参考这篇博文:点这里

当然了,有了无状态的迭代器,就有了有状态的迭代器了,有状态的迭代器就是专门用一个table来保存状态;在无状态的迭代器中,我们每一次都是迭代一个table,这个table就是一个无状态的table,它不会再遍历的过程中发生变化,而有状态的迭代器,则会在遍历的过程中对迭代的table进行变更,迭代的table的状态也随之发生了变化。这里不做详细的总结。

Lua中的迭代器与泛型for的更多相关文章

  1. Lua学习(5)——迭代器与泛型for

    1. 迭代器 2. 泛型for语义 所谓迭代器就是一种可以遍历一种集合中所有元素的机制.在lua中,迭代器通常表示为函数,每调用依次函数就返回集合中的下一个元素.泛型for 内部保存了迭代器函数 实际 ...

  2. Lua迭代器和泛型for

    1.迭代器与closure 在lua中,迭代器通常为函数,每调用一次函数,会返回集合中的下一个元素.每个迭代器在成功调用的时候,都需要保存一些状态,closure(闭包)完美为迭代器运用而生. fun ...

  3. lua迭代器和泛型for浅析

    (一) 首要概念要理清: 1. 在lua中,函数是一种"第一类值",他们具有特定的词法域."第一类值"表示在lua中函数与其他传统类型的值(例如数字和字符串)具 ...

  4. Step By Step(Lua迭代器和泛型for)

    Step By Step(Lua迭代器和泛型for) 1. 迭代器与Closure:    在Lua中,迭代器通常为函数,每调用一次函数,即返回集合中的"下一个"元素.每个迭代器都 ...

  5. Lua语法基础(3)--迭代器和泛型for

    迭代器和闭包 迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素.在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素. 迭代器需要保留上一次成功调用的状态和下一次成功 ...

  6. Lua中的语句

    [赋值] 赋值的基本含义是修改一个变量或一个table中字段的值,这个和其它语言没有多少区别,但是对于Lua,有一个特性,它允许“多重赋值”,也就是一下子将多个值赋予多个变量,例如以下代码: , pr ...

  7. Lua中的控制结构

    Lua提供了一组传统的.小巧的控制结构,包括用于条件执行的if,用于迭代的while.repeat和for.所有的控制结构都有意个显式的终止符:if.for和while以end作为结尾,repeat以 ...

  8. 两个函数彻底理解Lua中的闭包

    本文通过两个函数彻底搞懂Lua中的闭包,相信看完这两个函数,应该能理解什么是Lua闭包.废话不多说,上 code: --[[************************************** ...

  9. lua中遍历table的几种方式比较

    当我在工作中使用lua进行开发时,发现在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式不同,这四种方式分别是: for key, value in pairs(tbtes ...

随机推荐

  1. L1-8 矩阵A乘以B (15 分)

    给定两个矩阵A和B,要求你计算它们的乘积矩阵AB.需要注意的是,只有规模匹配的矩阵才可以相乘.即若A有R​a​​行.C​a​​列,B有R​b​​行.C​b​​列,则只有C​a​​与R​b​​相等时,两 ...

  2. spring @CrossOrigin解决跨域问题

    阅读目录: 一.跨域(CORS)支持: 二.使用方法: 1.controller配置CORS 2.全局CORS配置 3.XML命名空间 4.How does it work? 5.基于过滤器的CORS ...

  3. JAVA获取计算机CPU、硬盘、主板、网络等信息

    通过使用第三方开源jar包sigar.jar我们可以获得本地的信息 1.下载sigar.jar sigar官方主页 sigar-1.6.4.zip 2.按照主页上的说明解压包后将相应的文件copy到j ...

  4. 【学习总结】GirlsInAI ML-diary day-11-while循环

    [学习总结]GirlsInAI ML-diary 总 原博github链接-day11 认识while循环执行 对于while/break/continue的认识 新值替换变量 一般while语句 无 ...

  5. IdentityServer4【Reference】之Profile Service

    Profile Service 当创建令牌或者请求像Userinfo这种端点时,IdentityServer通常会需要用户的标识信息(identity information),默认情况下,Ident ...

  6. Linux(Ubuntu)使用日记(零)------使用Linux的理由

    我为什么要使用Linux呢,也许在某些人看来或许是装B,但是我的原因有点复杂,简单做下总结(以事情的发展历程为顺序) 升级后开机慢关机慢,崩溃.最近刚刚升级了windows,自己原来的win10其实是 ...

  7. Javascript初识之数据类型

    一.JavaScript概述 1.ECMAScript和JavaScript的关系 1996年11月,JavaScript的创造者--Netscape公司,决定将JavaScript提交给国际标准化组 ...

  8. mpvue——引入iconfont字体图标

    前言 有问题可以随时提问,评论私信,只要我有时间我都会第一时间回复.当大家发现这篇文章不适用的时候烦请告知下,我好做好更改! 放置 下载好的字体图标放在static目录下,我是自己又创建了一个icon ...

  9. [SCOI2006] 数字立方体

    题目类型:三维前缀和+同余方程 传送门:>Here< 题意:给出一个立方体,求有多少个子立方体的和为\(k\)的倍数 解题思路 暴力做法:\(O(n^6)\)枚举子立方体 考虑只枚举长和宽 ...

  10. saltstack主机管理项目:计主机管理项目命令分发器(三)

    一.开发目标命令格式如下: 二.目录结构 三.代码注解 01.salt.py 只是一个入口,没干什么事情 #!/usr/bin/env python # -*- coding:utf-8 -*- # ...