看看天空 发表于 2020-2-28 23:27

lua学习之迭代器与泛型for第一篇

# 迭代器与泛型 for 1





## 迭代器与 closure

1. 「迭代器」是一种可以遍历一种集合中的所有元素的机制
2. 在 lua 中通常将迭代器表示为**函数**
3. 每调用一次函数,即返回集合中的「下一个」元素
4. 每个迭代器都需要在每次成功调用之间保存一些**状态**
5. 这样就知道**它现在所在的位置**以及**如何步进到一下位置**
6. 一个 `closure` 就是一种可以访问**其外部嵌套环境中的局部变量**的函数
7. 这些变量可用于在成功调用之间保持状态值
8. 从而使 `closure` 可以记住它在一次遍历中所在的位置
9. 创建一个 `closure` 必须创建它的「非局部变量」
10. 一个`closure` 结构通常包含两个函数
    1. `closure` 本身
    2. 创建该 `closure` 的工厂函数



### while

1. `values` 就是工厂,每次调用这个工厂,就会创建一个新的 `closure` 即迭代器其本身
2. 这个 `closure` 将它的想要保存在其外部变量 `t` 和 `i` 中
3. 每当调用这个迭代器时,它就从列表 t 中返回下一个值
4. 直到最后一个元素返回后,迭代器会返回 `nil`,表示迭代的结束



```lua
function values(t)
    local i = 0
    return function ()
      i = i + 1
      return t
    end
end
t = {123, 333, 444}
iter = values(t) -- 创建迭代器
while true do
    local element = iter() -- 调用迭代器
    if element == nil then
      break
           end
    print(element)
end
```





### 泛型 for

1. 泛型 `for` 记录了每一次迭代循环
2. 它在内部保存了迭代器函数,因此不需要 `iter` 变量
3. 它在每次新迭代时调用迭代器,并在迭代器返回 `nil` 时循环结束



```lua
function values(t)
    local i = 0
    return function ()
      i = i + 1
      return t
    end
end
t = {123, 333, 444}
-- 泛型 for
t = {123, 333, 444}
for element in values(t) do
    print(element)
end
```



#### 高级用法

1. 遍历当前输入文件中所有单词的迭代器
2. 需要保持的值
   1. 当前行的内容
   2. 以及该行所处的位置
3. 使用 `string.find` 在当前行中调用,以当前位置作为起始位置来搜索一个单词
4. 使用模式 `%w+` 用来表示一个「单词」, 英语匹配一个或多个文字或数字字符
5. 如果找到了一个单词,迭代器将**当前位置更新**为该单词之后的第一个字符,并返回该单词
6. 否则,迭代器读取新的一行并重复这个搜索过程
7. 若没有剩余的行,则返回 nil, 以此表示迭代的结束。

```lua
-- 编写迭代器
function allwords()
        local line = io.read() -- 当前行
    local pos = 1 -- 一行中的位置
    return function () -- 迭代器函数
       while line do --只要 line 不为 nil 循环执行
            -- 返回开始位置和结束位置
            local start, end = string.find(line, "%w+", pos)
              if s then -- 是否找到一个单词
                    pos = e + 1 -- 找到一个单词,则移到这个单词的下一个位置
                    return string.sub(line, s) -- 返回该单词
            else
                    line = io.read() -- 这一行没找到,尝试读取下一行
                    pos = 1
            end
       end
       return nil -- 没有剩余行了,遍历结束
    end
end

-- 调用迭代器
for word in allwords() do
    print(word)
end
```



## 泛型 for 的语义



1. 上述的迭代器需要为每一个新的循环创建一个新的 `closure`,开销很大
2. 泛型 `for` 在循环过程内保存了迭代器函数
3. 保存了 3 个值
   1. 一个迭代器函数
   2. 一个恒定状态
   3. 一个控制变量



```lua
-- var-list 变量列表 , exp-list 表达式列表, 多个元素可用 , 逗号分割
-- 通常表达式列表只有一个元素,即只有一句对迭代器工厂的调用
for <var-list> in <exp-list> do
    <code block>
end

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

for line in io.lines() do
    io.write(line, "\n")
end
```

1. **变量列表的第一元素**称为「控制变量」,在循环过程中该值**绝不会为** `nil`,当它为 `nil` 时**循环结束**
2. `for` **首先**会先对 `in` 后面的表达式求值,这些**表达式应返回 3 个值**供 `for` 保存
   1. 迭代器函数
   2. 恒定状态
   3. 控制变量的初值
3. 类似多重赋值,即只有最后一个表达式才会产生多个结果
4. 且**只会保留前 3 个值**,多余的值被丢弃,不足的话,用 `nil` 补足
5. 在**初始化步骤后**,`for` 会以**恒定状态和控制变量**来调用迭代器函数
6. 然后 `for` 将**迭代器函数的返回值**赋予**变量列表中的变量**
7. 如果变量列表中的第一个元素(即**控制变量**)的返回值为 `nil`,则循环终止
8. 否则,`for` 执行循环体,然后再次调用迭代器函数,并重复这个过程
9. 从 `for` 构建的角度来说,恒定状态的内容与 `for` 本身是完全无关的。`for` 只是保存了初始化中返回的值,并在调用迭代器函数时传入该值。



```lua
for var_1, ..., var_n in <exp-list> do
    <code clock>
end

-- 上述代码等价于如下代码s
do
   -- _f 为迭代器函数,_s 为恒定状态,控制变量的初值为 a0
   local _f, _s, _var = <exp-list>
   while true do
      local var_1, ... var_n = _f(_s, _var)
      _var = var_1
      if _var == nil then
            break
      end
    end
    <code block>
end

```



1. `_f` 为迭代器函数,`_s` 为恒定状态,控制变量的初值为 `a₀`
2. 在循环过程中控制变量的值依次为 `a₁ = f(s, a₀)` 、`a₂ = f(s, a₁)` 以此类推
3. 直至 `ai` 为 `nil` 结束循环
4. 如果 `for` 还有其他变量,那么 他们也会在每次调用 `f` 后获得额外的值

angeljay 发表于 2020-2-28 23:41

先学习一下基础知识,感谢了

Rod.9800 发表于 2020-2-28 23:53

谢谢分享

fyh505099 发表于 2020-2-29 09:26

谢谢分享 好好学学一下
页: [1]
查看完整版本: lua学习之迭代器与泛型for第一篇