将协程用作迭代器
循环迭代器可以视为生产者-消费者模式的一种特例:一个迭代器会生产由循环体消费的内容。因此,用协程来实现迭代器也是自然的一种方式。同时,协程最关键的特性就是能够颠倒调用者与被调用这之间的关系。有了这种特性,我们在编写迭代器时就无须担心如何保存连续调用之间的状态了。
要编写这样一种迭代器并不容易,所以我们选择编写一个递归函数来产生所有的排列。思路也很简单,只要依次将每个数组元素放到最后一个位置,然后递归地生成其余元素的所有排列即可。
function permgen (a, n)
n = n or #a
if n <= 1 then
printResult(a)
else
for i = 1, n do
a[n], a[i] = a[i], a[n]
permgen(a, n - 1)
a[n], a[i] = a[i], a[n]
end
end
end
function printResult (a)
for i = 1, #a do io.write(a[i], " ") end
io.write("\n")
end
permgen({1, 2, 3, 4})
--有了生成器后,将其转换为迭代器就很容易了。首先,我们把printResult 改为 yield:
function permgen (a, n)
n = n or #a
if n <= 1 then
coroutine.yield(a)
else
for i =1, n do
a[n], a[i] = a[i], a[n]
permgen(a, n-1)
a[n], a[i] = a[i], a[n]
end
end
end
--然后我们定义一个将生成器放入协程运行并创建迭代函数的工厂。迭代器只是简单地唤醒协程,让其产生下一个排列:
function permutations (a)
local co = coroutine.create(function() permgen(a) end)
return function()
local code, res = coroutine.resume(co)
return res
end
end
--如此一来,在for循环中遍历一个数组的所有排列就非常简单了:
for p in permutations{"a", "b", "c"} do
printResult(p)
end
函数permutations使用了Lua语言中一种常见的模式,即将唤醒对应协程的调用包装在一个函数中,由于这种模式十分常见,Lua语言专门提供了一个特殊的函数coroutine.wrap来完成这个功能。函数wrap用来创建一个新的例程, 并返回一个函数,当这个函数被调用时就会唤醒协程。
function permutations (a)
return coroutine.wrap(function () permgen(a) end)
end