迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。在 Lua 中我们 常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。
闭包和迭代器
- 迭代器需要保留上一次成功调用的状态
- 迭代器需要保留下一次成功调用的状态
我们知道闭包可以保留每次调用的状态。并且可以方便的访问局部变量;所以我们也可以使用闭包来构建迭代器。
还记得我们在做 for 循环遍历表时的 pairs (t) 的迭代器吗?我们尝试自己来写一个迭代器:
-- 迭代器 | |
local function list_iter(t) | |
local i=0 | |
local n=#t | |
return function () | |
i=i+1 | |
if i<=n then | |
return t[i] | |
end | |
end | |
end | |
local t={4,5,8} -- 创建表 | |
local iter = list_iter(t) -- 创建迭代器变量实例 | |
while true do | |
local element=iter() -- 使用迭代器 | |
if element==nil then | |
break | |
end | |
print('数据为:'..element) | |
end |
Ps:如果要创建一个闭包必须要创建其外部局部变量。所以一个典型的闭包的结构包含两个函数:一个是闭包自己;另一个是工厂(创建闭包的函数)。
当然我们可以将这个迭代器应用于 for 循环中:
for value in list_iter(t) do
print(value)
end
迭代器小例子-访问文件
-- 打开文件 | |
local file = io.open('hh.txt',"r")-- 指定文件操作为读取 | |
if nil==file then | |
print('文件打开失败') | |
end | |
function ReadFile(file) | |
return function () | |
local line=file:read('*l') -- 以行的形式进行读取 | |
if nil==line then | |
return nil | |
else | |
return line | |
end | |
end | |
end | |
for data in ReadFile(file) do | |
print(data) | |
end | |
-- 使用 while 的话 | |
local readfile=ReadFile(file) | |
while true do | |
local line=readfile() | |
if line==nil then | |
break | |
end | |
print(line) | |
end |
深入了解for循环
在上面的闭包迭代器中我们不难发现,如果使用 for 循环则可以免去 while 循环所需的迭代闭包变量,
这样使用起来就比 while 循环要方便一些。那 for 循环是怎么做到的呢?
让我们来看看 for 循环的格式:for var_1, …, var_n in explist do block end
在 for 循环我们可以使用多个参数,如之前我们使用的系统自带的表迭代器,我们可以获得 k 和 v 的值。
在in
关键字后接需要被迭代的数据源列表(也就是说可以迭代多个列表),然后在do
后对遍历项进行处理。
Ps:其实在 lua 中,for 的执行顺序是这样的:
- 首先,初始化,计算 in 后面表达式的值,表达式应该返回范性 for 需要的三个值:迭代函数,状态常量和控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自动用 nil 补足,多出部分会被忽略。
- 第二,将状态常量和控制变量作为参数调用迭代函数(注意:对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
- 第三,将迭代函数返回的值赋给变量列表。
- 第四,如果返回的第一个值为 nil 循环结束,否则执行循环体。
- 第五,回到第二步再次调用迭代函数
从某种意义上来说,for 循环还是依赖于 while 循环做的优化。
其等价为:
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 |
无状态迭代器
上面我们依据闭包的思想实现的迭代器其核心思想就是保留状态,那有没有一种方式无需保留状态构建迭代器呢?
让我们回想一下,系统自带的pairs(t)
迭代器有使用到闭包吗?答案是:没有
所以pairs(t)
迭代器就是一种无状态迭代器~
我们尝试着实现一个 pairs 的效果,其代码如下:
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 | |
local tt={45,46,100} | |
for key, value in ipairs(tt) do | |
print(key,value) | |
end |
我们每次调用 ipairs 时,都自动去调用 iter,并且在调用时保留 iter 迭代函数,a 迭代数据,0 迭代索引。(很神奇吧,这种写法估计只有 lua 的作者才能想到,简直天才~)
Ps:同理,lua 中 ipairs 也是这样调用其他函数的(调用 next 函数)
多状态控制器
很多情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最
简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到 table 内,将 table
作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在 table 内,所以迭代函
数通常不需要第二个参数。
[toc]