第一版为 Lua 5.0 编写。虽然在很大程度上仍然适用于后续版本,但存在一些差异。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还将帮助 支持 Lua 项目。
Lua 编程 | ||
第一部分。语言 第 9 章。协程 |
我们可以将循环迭代器视为生产者-消费者模式的一个非常具体的示例。迭代器生成要由循环体使用的项。因此,使用协程来编写迭代器似乎很合适。实际上,协程为这项任务提供了一个强大的工具。同样,关键功能是它们能够颠倒调用者和被调用者之间的关系。借助此功能,我们可以编写迭代器,而无需担心如何在对迭代器的连续调用之间保持状态。
为了说明这种用法,我们编写一个迭代器来遍历给定数组的所有排列。直接编写此类迭代器并非易事,但编写生成所有这些排列的递归函数并不困难。这个想法很简单:依次将每个数组元素放在最后一个位置,并递归生成剩余元素的所有排列。代码如下
function permgen (a, n) if n == 0 then printResult(a) else for i=1,n do -- put i-th element as the last one a[n], a[i] = a[i], a[n] -- generate all permutations of the other elements permgen(a, n - 1) -- restore i-th element a[n], a[i] = a[i], a[n] end end end为了看到它的工作原理,我们应该定义一个适当的
printResult
函数,并使用适当的参数调用 permgen
function printResult (a) for i,v in ipairs(a) do io.write(v, " ") end io.write("\n") end permgen ({1,2,3,4}, 4)
在准备好生成器后,将其转换为迭代器是一项自动任务。首先,我们将 printResult
更改为 yield
function permgen (a, n) if n == 0 then coroutine.yield(a) else ...然后,我们定义一个工厂,安排生成器在协程内运行,然后创建迭代器函数。迭代器只是恢复协程以生成下一个排列
function perm (a) local n = table.getn(a) local co = coroutine.create(function () permgen(a, n) end) return function () -- iterator local code, res = coroutine.resume(co) return res end end有了这个机制,使用 for 语句遍历数组的所有排列就变得非常简单
for p in perm{"a", "b", "c"} do printResult(p) end --> b c a --> c b a --> c a b --> a c b --> b a c --> a b c
perm
函数使用 Lua 中的常见模式,该模式将对 resume 的调用与其相应的协程打包在一个函数中。此模式非常常见,以至于 Lua 为其提供了一个特殊函数:coroutine.wrap
。与 create
类似,wrap
创建一个新的协程。与 create
不同,wrap
不会返回协程本身;相反,它返回一个在调用时恢复协程的函数。与原始 resume
不同,该函数不会将其第一个结果作为错误代码返回;相反,它会在出现错误时引发错误。使用 wrap
,我们可以按如下方式编写 perm
function perm (a) local n = table.getn(a) return coroutine.wrap(function () permgen(a, n) end) end
通常,coroutine.wrap
比 coroutine.create
更易于使用。它为我们提供了协程所需的一切:一个用来恢复它的函数。但是,它也较不灵活。没有办法检查使用 wrap
创建的协程的状态。此外,我们无法检查错误。
版权所有 © 2003–2004 Roberto Ierusalimschy。保留所有权利。 |