例子:打印一个数组元素的所有的排列。
直接写这样一个迭代函数来完成这个任务并不容易,但是写一个生成所有排列的递归函数并不难。思路:将数组中的每一个元素放到最后,依次递归生成所有剩余元素的排列。
普通的loop实现代码:
function printResult(a)
for i = 1, #a do
io.write(a[i], ' ')
end
io.write('\n')
end
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]--交换a[i]和a[n]
permgen(a, n-1)
a[n], a[i] = a[i], a[n]
end
end
end
permgen({1,2,3})
迭代器实现,注意比较下代码的改变的部分:
第一步:将permgen中的printResult修改为yield函数
第二步:定义一个迭代工厂,修改生成器在生成器内创建迭代函数,并使生成器运行在一个协同程序内。迭代函数负责请求协同产生下一个可能的排列。
--定义迭代工厂,修改生成器在生成器内创建迭代函数,使生成器运行在一个协同程序内。迭代函数负责请求协同产生的下一个可能的排列。
function permutations(a)--迭代工厂
local co = coroutine.create(function () permgen(a) end)--生成器运行于协同程序内
return function ()--迭代器
local code, res = coroutine.resume(co)--在迭代器内resume,以产生下一个的permutations
return res
end
end
这样我们就可以使用for循环来打印出一个数组的所有排列情况了。
for p in permutations({"a", "b", "c"}) do
printResult(p)
end
完整代码:
function printResult(a)
for i = 1, #a do
io.write(a[i], ' ')
end
io.write('\n')
end
function permgen(a, n)
n = n or #a
if n <= 1 then
coroutine.yield(a)--注意此处的差别,上述例子是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 permutations(a)--注意此处的代码
local co = coroutine.create(function () permgen(a) end)
return function ()
local code, res = coroutine.resume(co)
return res
end
end
for p in permutations({"a", "b", "c"}) do
printResult(p)
end
运行结果如下:
permutations 函数使用了一个Lua中的常规模式,将在函数中去resume一个对应的coroutine进行封装。Lua对这种模式提供了一个函数coroutine.wap 。跟create 一样,wrap 创建一个新的coroutine ,但是并不返回给coroutine,而是返回一个函数,调用这个函数,对应的coroutine就被唤醒去运行。跟原来的resume 不同的是,该函数不会返回errcode作为第一个返回值,一旦有error发生,就退出了(类似C语言的assert)。使用wrap, permutations可以如下实现:
function permutations (a)
return coroutine.wrap(function () permgen(a) end)
end
wrap 比create 跟简单,它实在的返回了我们最需要的东西:一个可以唤醒对应coroutine的函数。 但是不够灵活。没有办法去检查wrap 创建的coroutine的status, 也不能检查runtime-error(没有返回errcode,而是直接assert)。