深入解读Lua中迭代器与泛型for的使用

 更新时间:2020年6月30日 23:50  点击:1699

泛型for原理

迭代器是一种可以遍历集合中所有元素的机制,在Lua中通常将迭代器表示为函数,每调用一次函数,就返回集合中“下一个”元素。每个迭代器都需要在每次成功调用之间保持一些状态,这样才能知道它所在的位置及如何步进到下一个位置,closure就可以完成此项工作。下面的示例是列表的一个简单的迭代器:

function values(t)
 local i = 0
 return function() i = i + 1; return t[i] end
end

循环调用:

t = {10, 20, 30}
iter = values(t)
while true do
 local el = iter()
 if el == nil then break end
 print(el)
end

泛型for调用

for el in values(t) do print(el) end

泛型for为一次迭代循环做了所有的簿记工作。它在内部保存了迭代器函数,并在每次迭代时调用迭代器,在迭代器返回nil时结束循环。实际上泛型for保存了3个值:迭代器函数f、恒定状态s、控制变量a。for做的第一件事就是对in后面的表达式求值,并返回3个值供for保存;接着for会以s和a来调用f。在循环过程中控制变量的值依次为a1 = f(s, a0),a2 = f(s, a1),依次类推,直至ai为nil结束循环。

先看一段代码

for element in list_iter(t) do 
 print(element) 
end 

在不往下看之前,我们可以先试图根据我们已有的知识结构去理解这段代码。如果这样,list_iter(t)应该返回一个类似集合的东西,而我们已经知道实际上只返回了一个匿名函数,也就是迭代器。当然,每次调用迭代器都可以得到一个元素,迭代器的所有的结果倒是可以看成一个集合。因素齐了,我们需要一个逻辑上的解释,这个逻辑就是 泛型for的语义。
先看文法规定:

for <var-list> in <exp-list> do 
 <body> 
end 

整个过程是这样的:
首先,初始化,计算 in 后面表达式的值,表达式应该返回 泛型for 需要的三个值:迭代函数、状态常量、控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自

动用nil 补足,多出部分会被忽略。
第二,将状态常量和控制变量作为参数调用迭代函数(注意:对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。

第三,将迭代函数返回的值赋给变量列表。
第四,如果返回的第一个值为 nil 循环结束,否则执行循环体。
第五,回到第二步再次调用迭代函数。

更具体地说:

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 

 泛型 for 在自己内部保存三个值:迭代函数、状态常量、控制变量。

迭代器的状态

无状态的迭代器本身不保存任何状态,for循环只会用恒定状态和控制变量来调用迭代器函数。这类迭代器典型例子就是ipairs,下面是ipairs的Lua实现:

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

当for循环调用ipairs(list)时,会获得3个值,然后Lua调用iter(list, 0)得到list, list[1],调用iter(list, 1)得到list, list[2],知道得到一个nil为止。

虽然泛型for只提供一个恒定状态和一个控制变量用于状态的保存,但有时需要保存许多其他状态。这时可以用closure来保存,或者将所需的状态打包为一个table,并保存在恒定状态中。

闭包、迭代器和泛型for

到现在,Lua为我们准备了三块积木:闭包、泛型for和迭代器。一个循环,我们可以利用闭包+迭代器,也可以使用泛型for+迭代器。那我们该怎么取舍呢?Lua也给出了建

议。

function iter (a, i) 
 i = i + 1 
 local v = a[i] 
 if v then 
  return i, v 
 end 
end 
 
function ipairs (a) 
 return iter, a, 0 
end 
 
for i, v in ipairs(a) do 
 print(i, v) 
end 

这种情况是Lua最推荐的,迭代器不依赖upvalue,不产生闭包,状态常量和控制变量借助泛型for保存,通过迭代器的参数传递给了迭代器。
再给一个书中的例子:

local iterator -- to be defined later 
 
function allwords() 
 local state = {line = io.read(), pos = 1} 
 return iterator, state 
end 
 
function iterator (state) 
 while state.line do -- repeat while there are lines 
  -- search for next word 
  local s, e = string.find(state.line, "%w+", state.pos) 
  if s then -- found a word? 
   -- update next position (after this word) 
   state.pos = e + 1 
   return string.sub(state.line, s, e) 
  else -- word not found 
   state.line = io.read() -- try next line... 
   state.pos = 1 -- ... from first position 
  end 
 end 
 return nil -- no more lines: end loop 
end 

这样好不好呢,Lua给的答案是否定的。书中有一段话说得很清楚:
我们应该尽可能的写无状态的迭代器,因为这样循环的时候由for 来保存状态,不需要创建对象花费的代价小;如果不能用无状态的迭代器实现,应尽可能使用闭包;尽可能不

要使用table 这种方式,因为创建闭包的代价要比创建table 小,另外Lua 处理闭包要比处理table 速度快些。

[!--infotagslink--]

相关文章

  • python-for x in range的用法(注意要点、细节)

    这篇文章主要介绍了python-for x in range的用法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-05-10
  • pytorch::Dataloader中的迭代器和生成器应用详解

    这篇文章主要介绍了pytorch::Dataloader中的迭代器和生成器应用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-30
  • Lua语言新手简单入门教程

    这篇文章主要给大家介绍的是关于Lua语言新手入门的简单教程,文中通过示例代码一步步介绍的非常详细,对各位新手们的入门提供了一个很方便的教程,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。...2020-06-30
  • lua读取redis数据的null判断示例代码

    最近在工作中遇到了一个问题,通过查找相关资料才得知原因是因为返回结果的问题,下面这篇文章主要给大家介绍了关于lua读取redis数据的null判断的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下...2020-06-30
  • forum.php怎么打开?

    本文介绍了forum.php的打开方法,不会的同学可以参考一下。 虚拟空间,网页管理端, 会有一个默认访问页面(找一下,是这个意思,可能会有所不同),内容一般是 index.html index...2017-07-06
  • Javascript for in的缺陷总结

    这篇文章主要介绍了Javascript for in的缺陷总结的相关资料,需要的朋友可以参考下...2017-02-08
  • Vue.js中轻松解决v-for执行出错的三个方案

    v-for标签可以用来遍历数组,将数组的每一个值绑定到相应的视图元素中去,下面这篇文章主要给大家介绍了关于在Vue.js中轻松解决v-for执行出错的三个方案,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。...2017-06-15
  • Navicat for MySQL 11注册码\激活码汇总

    Navicat for MySQL注册码用来激活 Navicat for MySQL 软件,只要拥有 Navicat 注册码就能激活相应的 Navicat 产品。这篇文章主要介绍了Navicat for MySQL 11注册码\激活码汇总,需要的朋友可以参考下...2020-11-23
  • shell中的for循环用法详解

    这篇文章主要介绍了shell中的for循环用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-07-11
  • Lua判断变量是否为数字、字符串是否可以转换为数字等

    这篇文章主要介绍了Lua判断变量是否为数字、字符串是否可以转换为数字等,本文讲解了Lua 判断是字符还是数字的方法、Lua判断数字的方法、判断可否转换为数字的方法、判断并且准备一个初值的方法,需要的朋友可以参考下...2020-06-30
  • python for循环赋值问题

    这篇文章主要介绍了python for循环赋值问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-03
  • Lua流程控制语句if else的使用示例

    今天小编就为大家分享一篇关于Lua流程控制语句if else的使用示例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...2020-06-30
  • Lua的table库函数insert、remove、concat、sort详细介绍

    这篇文章主要介绍了Lua的table库函数insert、remove、concat、sort详细介绍,本文分别给出了这几个函数的使用实例,需要的朋友可以参考下...2020-06-30
  • vue v-for 点击当前行,获取当前行数据及event当前事件对象的操作

    这篇文章主要介绍了vue v-for 点击当前行,获取当前行数据及event当前事件对象的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-10
  • Lua中的模块(module)和包(package)详解

    这篇文章主要介绍了Lua中的模块(module)和包(package)详解,本文讲解了require函数、写一个模块、package.loaded、module函数等内容,需要的朋友可以参考下...2020-06-30
  • Lua中的loadfile、dofile、require详解

    这篇文章主要介绍了Lua中的loadfile、dofile、require详解,本文分别用实例讲解它的用法和特点等内容,需要的朋友可以参考下...2020-06-30
  • JavaScript中for循环的几种写法与效率总结

    每个接触JS的开发人员都不可避免的与for循环打交道,毕竟这是遍历必不可少的工具之一。然而当循环次数比较大时,效率问题必须重视。下面这篇文章就主要介绍了JavaScript中几种for循环的写法与效率,需要的朋友可以参考借鉴,下面来一起看看吧。...2017-02-08
  • C# for循环的经典案例集锦

    本篇文章主要介绍了关于for循环的经典案例,具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • 如何使用Vim搭建Lua开发环境详解

    这篇文章主要给大家介绍了关于如何使用Vim搭建Lua开发环境的相关资料,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-30
  • Vue中的v-for列表循环示例详解

    循环使用v-for指令,v-for指令需要以siteinsites形式的特殊语法,sites是源数据数组并且site是数组元素迭代的别名,下面这篇文章主要给大家介绍了关于Vue中v-for列表循环的相关资料,需要的朋友可以参考下...2022-11-01