迭代器

  • 遍历一个集合所有元素代码结构

    • Lua中,使用函数表示迭代器,每调用一次,返回集合的下一个元素
    • 迭代器需要在每次调用之间保存迭代所处位置状态

      闭包

  • 访问自身环境中局部变量的函数,调用中的值保存在闭包中

    • 返回值是函数函数调用即为创建一个函数(闭包),但是调用函数中的变量仍然可以调用
    • 闭包的创建:
      • 创建非局部变量
      • 创建闭包本身函数
      • 创建该闭包及其封装变量工厂 ```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创建 —]]

  1. ```lua
  2. --一个简单的迭代器
  3. function values(t)
  4. local i = 0
  5. return function()
  6. i = i + 1
  7. return t[i]
  8. end
  9. end
  10. --[[
  11. 该迭代器中,values为工厂,调用可创建一个闭包,
  12. 状态保存在t和i中,t和i也由values创建
  13. --]]
  14. t = {10, 20, 30}
  15. iter = values(t) --创建迭代器,迭代器为iter,也创建了闭包
  16. while true do
  17. local element = iter() --调用迭代器
  18. if element == nil then
  19. break
  20. end
  21. print(element)
  22. end
  23. --[[
  24. 使用泛型for,不需要向while一样考虑迭代器的调用位置和迭代的跳出,
  25. 会在每次迭代时调用。
  26. --]]
  27. t = {10, 20, 30}
  28. for element in values(t) do
  29. print(element)
  30. end

泛型for

  • 为迭代循环记录工作:内部保存迭代函数,因此不需要每次迭代时,都调用迭代器

    • 使用泛型for,去除了创建闭包开销
    • 泛型for内部保存
      1. 迭代函数
      2. 不可变状态(invariant state)
      3. 控制变量(control variable)
    • 语法

      for var-list in exp-list do
      body
      end
      
      • var-list:一个或多个变量名组成的列表,逗号分隔
        • 第一个控制变量,在循环中永远不为nil为nil结束
      • exp-list:一个或多个表达式组成的列表,逗号分隔
        • 通常只有一个,即迭代器工厂
    • 泛型for的执行过程:
      1. 对in后的表达式求值
      2. 表达式提供3个值给for保存迭代函数不可控状态控制变量的初始值(顺序不能改变)
        • 在存在多个表达式时,只有最后一个表达式才可返回多个值(多重赋值)
      3. 表达式列表保留3个值丢弃不足补nil
      4. 使用 不可变状态控制变量 调用迭代函数(实际上不可变状态没有意义)
      5. 迭代函数返回值赋给 变量列表 的变量
      6. 如果第一个返回值为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)