迭代器和闭包

简介: 迭代器和闭包

迭代器iterator )是一种可以让我们遍历一个集合中所有元素的代码结构。在 Lua 语言中,通常使用函数表示迭代器:每一次调用函数时,函数会返回集合中的“下一个”元素。一个典型的例子就是 io.read ,每次调用该函数时它都会返回标准输入中的下一行,在没有可以读取的行时返回 nil


所有的迭代器都需要在连续的调用之间保存一些状态,这样才能知道当前迭代所处的位置及如何从当前位置步进到下一位置。对于函数 io.read 而言,C语言会将状态保存在流的结构体中。对于我们自己的迭代器而言,闭包则为保存状态提供了一种良好的机制。请注意:一个闭包就是一个可以访问其自身的环境中一个或多个局部变量的函数。这些变量将连续调用过程中的值并将其保存在闭包中,从而使得闭包能够记住迭代所处的位置。当然,要创建一个新的闭包,我们还必须创建非局部变量。因此,一个闭包结构通常涉及两个函数:闭包本身和一个用于创建该闭包及其封装变量的工厂factory )。


作为示例,让我们来为列表编写一个简单的迭代器。与 ipairs 不同的是,该迭代器并不是返回每个元素的索引值而是返回元素的值:

function values (t)
  local i = 0
  return function () i = i +1; return t[i] end
end


在这个例子中, values 就是工厂。每当调用这个工厂时,它就会创建一个新的闭包(即迭代器本身)。这个闭包将它的状态保存在其外部的变量 ti 中,这两个变量也是由 values 创建的。每次调用这个迭代器时,它就从列表 t 中返回下一个值。在遍历完最后一个元素后,迭代器返回 nil ,表示迭代结束。


我们可以在一个 while 循环中使用这个迭代器:

t = {10, 20, 30}
iter = values(t)                      -- 创建迭代器
while true do
  local element = iter()              -- 调用迭代器
  if element == nil then break end
  print(element)
end


不过,使用泛型for更简单。毕竟,泛型for正是为了这种迭代而设计的:

t = {10, 20, 30}
for element in values(t) do
  print(element)
end


泛型for为一次迭代循环做了所有的记录工作:它在内部保存了迭代函数,因此不需要变量 iter ;它在每次做新的迭代时都会再次调用迭代器,并在迭代器返回 nil 时结束循环。


来看一个更高级的示例,这个示例展示了一个迭代器,它可以遍历来自标准输入的所有单词:

function allwords()
  local line = io.read()                                    -- 当前行
  local pos = 1                                             -- 当前行的当前位置
  return function()                                         -- 迭代函数
    while line do                                           -- 当还有行时循环
      local w, e = string.match(line, "(%w+)()", pos)       
      if w then                                             -- 发现一个单词?
        pos = e                                             -- 下一个位置位于该单词后
        return w                                            -- 返回该单词
      else
        line = io.read()                                    -- 没找到单词,尝试下一行
        pos = 1                                             -- 从第一个位置重新开始
      end
    end
    return nil                                              -- 没有行了,迭代结束
  end
end


为了完成这样的遍历,我们需要保存两个值:当前行的内容(遍历 line )及当前行的当前位置(遍历 pos )。有了这些数据,我们就可以不断产生下一个单词。这个迭代函数的主要部分是调用函数 string.match ,以当前位置作为起始在当前行中搜索一个单词。函数 string.match 使用模式 '%w+' 来匹配一个单词,也就是匹配一个或多个字母/数字字符。如果函数 string.match 找到了一个单词,它就捕获并返回这个单词及该单词之后的第一个字符的位置(一个空匹配),迭代函数则更新当前位置并返回该单词,否则,迭代函数读取新的一行,然后重复上述搜索过程。在所有的行都被读取完后,迭代函数返回 nil 以表示迭代结束。


尽管迭代器本身有点复杂,但allwords的使用还是很简明易懂的:

for word in allwords() do
  print(word)
end


对于迭代器而言,一种常见的情况就是:编写迭代器可能不太容易,但使用迭代器却十分简单。这也不是一个大问题,因为使用 Lua 语言编程的最终用户一般不会取定义迭代器,而只会使用那些宿主应用已经提供的迭代器。

目录
相关文章
|
6月前
什么是闭包?手写一个闭包函数
什么是闭包?手写一个闭包函数
50 0
|
6月前
|
算法 程序员 C语言
【C++ 迭代器实现细节 】深入探索C++迭代器:从实现到整合
【C++ 迭代器实现细节 】深入探索C++迭代器:从实现到整合
172 0
|
算法 C++
82 C++ - 函数对象
82 C++ - 函数对象
40 0
|
存储 编译器 C语言
论函数对象
论函数对象
|
索引
在循环内使用闭包(Closures)
在循环内使用闭包(Closures)
80 0
|
算法 区块链 C++
迭代器与仿函数
迭代器与仿函数
|
存储 缓存 算法
【Python高级编程】—— 函数式编程总结(生成器、迭代器、匿名函数、高阶函数、返回函数、闭包、装饰器、偏函数)
Python函数式编程的一般概念及特点, 迭代器、生成器表达式、内置函数、常用高阶函数、递归与归约、实用模块和装饰器的用法, 以及避开Python严格求值顺序的变通方法、Web服务设计方法和一些优化技巧。
301 1
【Python高级编程】—— 函数式编程总结(生成器、迭代器、匿名函数、高阶函数、返回函数、闭包、装饰器、偏函数)
|
存储 PHP 开发者
lterator 迭代器|学习笔记
快速学习 lterator 迭代器
lterator 迭代器|学习笔记
|
Swift
闭包(特殊的函数)
闭包(特殊的函数)
122 0