迭代器
遍历一个集合中所有元素的代码结构
访问自身环境中局部变量的函数,调用中的值保存在闭包中
- 即返回值是函数在函数调用即为创建一个函数(闭包),但是调用的函数中的变量仍然可以调用
- 闭包的创建:
- 创建非局部变量
- 创建闭包本身函数
- 创建该闭包及其封装变量的工厂 ```lua —一个简单的闭包 function show() local i = 0 —在调用后,由于匿名函数中会使用到i,因此i不会被清除,成为了非局部变量 return function() print(i) i = i + 1 end end
local s = show()
—创建闭包:虽然show()函数结束,但是本次调用show()中的i仍然存在,即为非局部变量
—同样创建了创建迭代器s
s() —输出:0 s() —输出:1 s() —输出:2 —[[ 该例子中,show为工厂,调用该工厂时,会创建一个闭包, 状态保存在i中,i也由show创建 —]]
```lua
--一个简单的迭代器
function values(t)
local i = 0
return function()
i = i + 1
return t[i]
end
end
--[[
该迭代器中,values为工厂,调用可创建一个闭包,
状态保存在t和i中,t和i也由values创建
--]]
t = {10, 20, 30}
iter = values(t) --创建迭代器,迭代器为iter,也创建了闭包
while true do
local element = iter() --调用迭代器
if element == nil then
break
end
print(element)
end
--[[
使用泛型for,不需要向while一样考虑迭代器的调用位置和迭代的跳出,
会在每次迭代时调用。
--]]
t = {10, 20, 30}
for element in values(t) do
print(element)
end
泛型for
会为迭代循环做记录工作:内部保存迭代函数,因此不需要每次迭代时,都调用迭代器
- 使用泛型for,去除了创建闭包的开销
- 泛型for内部保存:
- 迭代函数
- 不可变状态(invariant state)
- 控制变量(control variable)
语法:
for var-list in exp-list do body end
- var-list:一个或多个变量名组成的列表,逗号分隔
- 第一个为控制变量,在循环中永远不为nil,为nil时结束
- exp-list:一个或多个表达式组成的列表,逗号分隔
- 通常只有一个,即迭代器工厂
- var-list:一个或多个变量名组成的列表,逗号分隔
- 泛型for的执行过程:
- 对in后的表达式求值
- 表达式提供3个值给for保存:迭代函数、不可控状态和控制变量的初始值(顺序不能改变)
- 在存在多个表达式时,只有最后一个表达式才可返回多个值(多重赋值)
- 表达式列表保留3个值(多的丢弃,不足补nil)
- 使用 不可变状态 和 控制变量 调用迭代函数(实际上不可变状态没有意义)
- 将迭代函数的返回值赋给 变量列表 的变量
- 如果第一个返回值为nil,则中止;否则继续for循环,迭代函数的第一个返回值作为控制变量 ```lua —将上面使用闭包迭代器的代码修改为不使用闭包的泛型for local t = {10, 20, 30} function values(t, i) —迭代函数 i = i + 1 if t[i] == nil then —第一个返回值为nil,则泛型for结束 return nil, nil else return i, t[i] —返回 不可控状态 和 控制变量 给变量列表 end end
function OutValues(t) —表达式,返回迭代函数、不可控状态和控制变量(values, t, 0) return values, t, 0 —第二次迭代时,控制变量就变为第一次迭代函数返回的第一个值 end
for k, v in OutValues(t) do print(k, v) end
<a name="dAacL"></a>
#### 无状态迭代器
- **自身不保存任何状态**的迭代器(**不创建闭包**保存状态)
- 无状态迭代器**根据** **不可变状态** 和 **控制变量** 来**迭代**生成**下一个元素**(上面一块代码也为无状态迭代器)
- 例1:**ipairs函数**就是典型的无状态迭代器
```lua
local t = {10, 20, 30, 40, 50, 60}
for k, v in ipairs(t) do --不可变状态:t;控制变量:当前索引
print(k, v)
end
- 例2:**pairs函数**
local t = {10, 20, 30, 40, 50, 60}
for k, v in pairs(t) do --不可变状态:t;控制变量:当前索引
print(k, v)
end
ipairs VS pairs
ipairs():迭代table有序元素
- ipairs(table):迭代table中所有有序元素,即索引为1~n的元素;如果索引不是整形,则不会迭代
- table不能为nil,索引不能不连续;如:索引没有1,2,3而是10,20,30 ```lua local t = {10, 20, i = 1, j = 2, [1.1] = 3, string = 4}
for k, v in ipairs(t) do print(k, v) end —[[ 输出: 1 10 2 20 —]]
<a name="Ft3fY"></a>
#### pairs():迭代table所有元素
- **pairs(table)**:**迭代**table中**所有元素**,如果**索引为整数**,则按**顺序输出**;如果索引**非整数**,则**随机输出**
- **pairs函数**中的**迭代函数**是**next(t,k)**,该函数会**随机返回**table中**下一个键**,和**k对应的值**
- t:table
- k:当前的键
```lua
local t = {10, 20, i = 1, j = 2, [1.1] = 3, string = 4}
for k, v in pairs(t) do
print(k, v)
end
--[[
1 10
2 20
string 4
1.1 3
i 1
j 2
--]]
- 注:一般不会定义迭代器,只是用宿主应用提供的迭代器
按循序循环无需表
- 使用pairs函数遍历表,会使结果随机出现(因为使用了next作为迭代函数)
- 因此在需要对键无顺序的table进行有序的输出时,方法如下:
- 把键拷贝到一个新数组中
- 然后对新数组进行排序 ```lua lines = { luaH_set = 24, luaH_get = 10, luaH_present = 48 }
a = {} —新table for k, v in pairs(lines) do —无序遍历,储存在新table中 a[#a + 1] = k print(k, v) end
table.sort(a) —更具旧table的键排序 for k, v in ipairs(a) do —输出 print(v) end
function pairByKeys(t, f) —循序遍历tabl的迭代器 local a = {} for k, v in pairs(t) do a[#a + 1] = k end table.sort(t, f) local i = 0 return function() i = i + 1 return a[i], t[a[i]] end end
for k, v in pairByKeys(lines) do print(k, v) end
<a name="OFPGw"></a>
# 真正的迭代器
- 在老版的Lua中,没有泛型for语句;因此真正的迭代器是for循环完成的
- 迭代器**接收**一个**函数作为参数**,这个**函数**在**循环**的**内部被调用**,这种迭代器被称为真正的迭代器
```lua
function allwords(f)
for line in io.lines() do
for word in line do
for word in string.gmatch(line, "%w+") do
f(word) --调用 接收的函数
end
end
end
end
local count = 1
allwords(
function(word) --将函数传入迭代器
if word == "hello" then
count = count + 1
end
end
)
print(count)