第一版是針對 Lua 5.0 編寫的。雖然在很大程度上仍然適用於後續版本,但有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買這本書,您也可以幫助支援 Lua 專案。
![]() |
程式設計 in 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
容易使用。它提供我們從 coroutine 所需的內容:一個用於繼續運作的函式。然而,它也比較不靈活。無法檢查使用 wrap
建立的 coroutine 的狀態。此外,我們也無法檢查錯誤。
版權所有 © 2003–2004 Roberto Ierusalimschy。保留所有權利。 | ![]() |