【前言】

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

  1. function values(tb)
  2. local i =
  3. return function ()
  4. i = i +
  5. return tb[i]
  6. end
  7. end
  8.  
  9. local testTb = {, , }
  10. for value in values(testTb) do
  11. print(value)
  12. end

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

【泛型for的语义】

泛型for比较复杂,它在循环过程内保存了迭代器函数。它实际上保存着3个值:一个迭代器函数、一个恒定状态和一个控制变量。接下来,分别进行总结。
泛型for的语法如下:

  1. for <var-list> in <exp-list> do
  2. <body>
  3. end

其中,<var-list>是一个或多个变量名的列表,以逗号分隔;<exp-list>是一个或多个表达式的列表,同样以逗号分隔。通常表达式列表只有一个元素,即一句对迭代器函数的调用。例如:

  1. for k, v in pairs(t) do print(k, v) end

for做的第一件事就是对in后面的表达式求值,这些表达式应该返回3个值供for保存:迭代器函数、恒定状态和控制变量的初值。这里和多重赋值是一样的,只有最后一个表达式才会产生多个结果,并且只会保留前3个值,多余的值会被丢弃;而不够的话,就以nil补足。

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

  1. for var_1, ..., var_n in <explist> do <block> end -- 就等价于以下代码:
  2. do
  3. local _f, _s, _var = <explist> -- 返回迭代器函数、恒定状态和控制变量的初值
  4. while true do
  5. local var_1, ..., var_n = _f(_s, _var)
  6. _var = var_1
  7. if _var == nil then break end
  8. <block>
  9. end
  10. end
  11. end

【无状态的迭代器】

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

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

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

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

  1. local function iter(a, i)
  2. i = i +
  3. local v = a[i]
  4. if v then
  5. return i, v
  6. end
  7. end
  8.  
  9. function ipairs(a)
  10. return iter, a,
  11. end

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

  1. function pairs(a)
  2. return next, t, nil
  3. 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. iOS开发基础-图片切换(4)之懒加载

    延续:iOS开发基础-图片切换(3),对(3)里面的代码用懒加载进行改善. 一.懒加载基本内容 懒加载(延迟加载):即在需要的时候才加载,修改属性的 getter 方法. 注意:懒加载时一定要先判断该 ...

  2. git完全cli指南之详细思维导图整理分享

    一直以来都觉得 在开发过程中 能用命令行的还是用命令行 能用快捷键的就要快捷键 前期可能要点学习成本 但是熟练后带来的好处还是非常可观的 所以一直坚持使用命令行的方式来使用git 基本上每个操作都能心 ...

  3. linux下开启、关闭、重启mysql服务命令

    一. 启动1.使用 service 启动:service mysql start2.使用 mysqld 脚本启动:/etc/inint.d/mysql start3.使用 safe_mysqld 启动 ...

  4. 通过sort()方法实现升序和降序排列

    数组 升序:Arrays.sort(arr); 降序: 方法一:Arrays.sort(arr,Collections.reverseOrder()); 方法二: package com.yh.sor ...

  5. MySQL索引原理及慢查询优化(转自:美团tech)

    背景 MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所谓“好马配好鞍”,如何能够更好的使用它,已经成为开发工程师的必修课,我们经常会 ...

  6. 控制结构(11): Continuation passing style(CPS)

    // 上一篇:控制结构(10)指令序列(opcode) [注释]: 这个笔记系列需要告一个段落了,收尾部分整理下几个时髦(The New Old Things)结构. 后面打算开一个算法方面的,重新学 ...

  7. 安装Java和Tomcat

    安装Java 下载java源码包 安装的是JDK8,下载地址如下:下载链接 注意,不要在服务器中使用wget来下载jdk,因为oracle会认为你是爬虫,下载的文件不是jdk,而是一个html文件. ...

  8. Ubuntu16.04安装TensorFlow及Mnist训练

    版权声明:本文为博主原创文章,欢迎转载,并请注明出处.联系方式:460356155@qq.com TensorFlow是Google开发的开源的深度学习框架,也是当前使用最广泛的深度学习框架. 一.安 ...

  9. 老婆大人 split,slice,splice,replace的用法

    split()方法用于把一个字符串分割成字符串数组 str.split("字符串/正则表达式从该参数制定额地方分割str",可选,可指定返回数组的最大长度,如果没设置参数,整个字符 ...

  10. Qt QWidget

    原文: https://www.cnblogs.com/muyuhu/archive/2012/10/26/2741184.html QWidget 类代表一般的窗口,其他窗口类都是从 QWidget ...