12、Lua 教程 - 迭代器

2019-07-03

迭代器是用于遍历集合或容器中元素的一种结构。在 Lua 语言中,集合往往指的是可以用来创建各种数据结构的表。比如,数组就是用表来创建的。

通用迭代器

通用迭代器可以访问集合中的键值对。下面是通用迭代器的一个简单例子:

array = {"Lua", "Tutorial"}

for key,value in ipairs(array) 
do
   print(key, value)
end

执行的上面的代码,我们可以得到如下的输出结果:

1  Lua
2  Tutorial

上面的例子中使用了 Lua 提供的默认迭代器函数 ipairs。

在 Lua 语言中,我们使用函数表示迭代器。根据是否在迭代器函数中是否维护状态信息,我们将迭代器分为以下两类:

  • 无状态迭代器
  • 有状态迭代器

无状态迭代器

由此迭代器的名称就可以看出来,这一类的迭代器函数中不会保存任何中间状态。

让我们一起来看一下下面这个例子。在这个例子中,我们用一个简单的函数创建了一个自己的迭代器。这个迭代器用以输出 n 个数的平方值。

function square(iteratorMaxCount,currentNumber)
   if currentNumber<iteratorMaxCount
   then
      currentNumber = currentNumber+1
   return currentNumber, currentNumber*currentNumber
   end
end

for i,n in square,3,0
do
   print(i,n)
end

执行上面的代码,我们可以得到如下的输出结果:

1   1
2   4
3   9

我们可以稍微的修改一下上面的代码,使得此迭代器可以像 ipairs 那样工作。如下所示:

function square(iteratorMaxCount,currentNumber)
   if currentNumber<iteratorMaxCount
   then
      currentNumber = currentNumber+1
   return currentNumber, currentNumber*currentNumber
   end
end

function squares(iteratorMaxCount)
   return square,iteratorMaxCount,0
end  

for i,n in squares(3)
do 
    print(i,n)
end

执行上面的代码,我们可以得到如下的输出结果:

1   1
2   4
3   9

有状态迭代器

前面的例子使用的迭代器函数是不保存状态的。每次调用迭代器函数时,函数基于传入函数的第二个变量访问集合的下一个元素。在 Lua 中可以使用闭包来存储当前元素的状态。闭包通过函数调用得到变量的值。为了创建一个新的闭包,我们需创建两个函数,包括闭包函数本身和一个工厂函数,其中工厂函数用于创建闭包。

下面的示例中,我们将使用闭包来创建我们的迭代器。

array = {"Lua", "Tutorial"}

function elementIterator (collection)
   local index = 0
   local count = #collection
   -- 返回闭包函数
   return function ()
      index = index + 1
      if index <= count
      then
         -- 返回迭代器的当前元素
         return collection[index]
      end
   end
end

for element in elementIterator(array)
do
   print(element)
end

执行上面的代码,我们可以得到如下的输出结果:

Lua
Tutorial

上面的例子中我们可以看到,在 elementIterator 函数内定义了另外一个匿名函数。此匿名函数中使用了一个外部变量 index (译注:此变量在匿名函数之外,elementIterator 函数内)。每次内部的匿名函数被调用时,都会将 index 的值增加 1,并统计数返回的每个元素。

我们可以参照上面的方法使用闭包创建一个迭代器函数。每次我们使用迭代器遍历集合时,它都可以返回多个元素。