Lua-基础(四)迭代器

发布于 2020-02-03  90 次阅读


迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。在 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的执行顺序是这样的:

  1. 首先,初始化,计算 in 后面表达式的值,表达式应该返回范性 for 需要的三个值:迭代函数,状态常量和控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自动用 nil 补足,多出部分会被忽略。
  2. 第二,将状态常量和控制变量作为参数调用迭代函数(注意:对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
  3. 第三,将迭代函数返回的值赋给变量列表。
  4. 第四,如果返回的第一个值为 nil 循环结束,否则执行循环体。
  5. 第五,回到第二步再次调用迭代函数

从某种意义上来说,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 内,所以迭代函
数通常不需要第二个参数。