programming in lua (4)

第 7 章。这一章看得不是很明白。

迭代器可以遍历一个集合中的所有元素。在 lua 中,迭代器一般都是函数,每次调用这个函数都会返回集合中的“下一个”元素,前面提到的闭包函数就是实现迭代器的很好选择:

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

arr = {"one", "two", "three"}

iter = values(arr)
while true do
   local e = iter()
   if e == nil then
      break
   end
   print(e)
end

函数 values() 返回一个匿名函数,每次调用这个匿名函数都会返回传给 values() 的“下一个”值。更常见的情况是写成“for ... in ...”的形式:

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

arr = {"one", "two", "three"}

for it in values(arr) do
   print(it)
end

for 语句保存了 values()
返回的匿名函数,并且把每次调用匿名函数返回的值保存在变量 it 中,直到 it 为 nil 为止。在这里 for 循环保存了三个隐含变量:匿名函数,不变量(在循环中不会改变)和控制变量。一般来说,for 循环的形式是:

for <var-list> in <exp-list> do
   <body>
end

这里 var-list 和 exp-list 都是用逗号分隔的变量。

for 语句做的第一件事是计算“in”后表达式的值,然后把不变量和控制变量传递给匿名函数作为参数调用匿名函数,把返回值依次赋给 var-list 中的变量。接着判断 var-list 中第一个变量是否为空,不为空的话继续执行 for 中的 body 部分。例如语句:

for var_1, var_2, ..., var_n in <exp-list> do <block> end

等价于下面的形式:

do
   local func, invariant, var = <exp-list>
   while true do
      local var_1, var_2, ..., var_n = func(invariant, var)
      var = var_1
      if var == nil then
         break
      end
      <block>
   end
end

因此前面的 values() 函数等价于:

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

arr = {"one", "two", "three"}

do
   local func, invariant, var = values(arr), arr
   while true do
      local it = func(invariant, var)
      var = it
      if var == nil then
         break
      end
      print(it)
   end
end

另外有一种是无状态的迭代器,它不会保存任何状态。每次循环调用迭代函数并且把不变量和控制变量传递给它,然后获取迭代函数的返回值。一个无状态迭代器的例子就是 ipairs():

a = {"one", "two", "three"}
for i, v in ipairs(a) do
   print(i, v)
end

使用迭代器实现:

function iter(a, i)
   i = i + 1
   if a[i] then
      return i, a[i]
   end
end

function ipairs(a)
   return iter, a, 0
end

arr = {"one", "two", "three"}

for i, v in ipairs(arr) do
   print(i, v)
end

在一个迭代器中经常要保存很多状态,最简单的做法是使用闭包,另一种做法是把要保存的内容都放到一个 table 中,并且可以改变 table 中元素的值。

上面使用的术语“迭代器”(iterator)有歧义,其实叫“生成器”(generator)更合适,因为每次调用它只是返回“下一个”值。另一种创建迭代器的方法就是写一个接收一个函数作为参数(也就是所谓的回调函数)的函数,在主函数中有一个循环,每次循环都调用这个回调函数。举个例子:

function real_iter(f)
   local i = 0
   while i < 5 do
      f(i)
      i = i + 1
   end
end

real_iter(print)

print("--------------------")

function func5(v)
   print(v + 5)
end

real_iter(func5)

函数 real_iter() 从 0 到 4,每次把当前的值传给回调函数 f。第 9 行 print() 作为参数,依次打印 i 的值;第 17 行 func5() 作为参数,打印 (i+5) 的值。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注