建立並使用協程

協同程式表中可以使用與協同程式互動的所有功能。通過使用帶有單個引數的 coroutine.create 函式建立一個新的協 :一個帶有要執行的程式碼的函式:

thread1 = coroutine.create(function()
            print("honk")
        end)

print(thread1)
-->> thread: 6b028b8c

一個 coroutine 物件返回 thread 型別的值,表示一個新的 coroutine。建立新協程時,其初始狀態將被暫停:

print(coroutine.status(thread1))
-->> suspended

要恢復或啟動協程,使用函式 coroutine.resume ,給出的第一個引數是執行緒物件:

coroutine.resume(thread1)
-->> honk

現在協程執行程式碼並終止,將其狀態更改為 dead ,無法恢復。

print(coroutine.status(thread1))
-->> dead

由於 coroutine.yield 函式,協同程式可以暫停其執行並在以後恢復它 :

thread2 = coroutine.create(function()
        for n = 1, 5 do
            print("honk "..n)
            coroutine.yield()
        end
    end)

如你所見,coroutine.yield() 存在於 for 迴圈中,現在當我們恢復協程時,它將執行程式碼,直到它到達 coroutine.yield:

coroutine.resume(thread2)
-->> honk 1
coroutine.resume(thread2)
-->> honk 2

在完成迴圈後,執行緒狀態變成,不能恢復。協同程式還允許資料之間的交換:

thread3 = coroutine.create(function(complement)
    print("honk "..complement)
    coroutine.yield()
    print("honk again "..complement)
end)
coroutine.resume(thread3, "stackoverflow")
-->> honk stackoverflow

如果在沒有額外引數的情況下再次執行協程,則補碼仍將是第一個簡歷中的引數,在本例中為 stackoverflow

coroutine.resume(thread3)
-->> honk again stackoverflow

最後,當一個協程結束時,其函式返回的任何值都會轉到相應的簡歷:

thread4 = coroutine.create(function(a, b)
    local c = a+b
    coroutine.yield()
    return c
end)
coroutine.resume(thread4, 1, 2)
print(coroutine.resume(thread4))
-->> true, 3

協程在此函式中用於在遞迴呼叫中從深處將值傳遞迴呼叫執行緒。

local function Combinations(l, r)
    local ll = #l
    r = r or ll
    local sel = {}
    local function rhelper(depth, last)
        depth = depth or 1
        last = last or 1
        if depth > r then
            coroutine.yield(sel)
        else
            for i = last, ll - (r - depth) do
                sel[depth] = l[i]
                rhelper(depth+1, i+1)
            end
        end
    end
    return coroutine.wrap(rhelper)
end

for v in Combinations({1, 2, 3}, 2) do
    print("{"..table.concat(v, ", ").."}")
end
--> {1, 2}
--> {1, 3}
--> {2, 3}

協同程式也可用於惰性評估。

-- slices a generator 'c' taking every 'step'th output from the generator
-- starting at the 'start'th output to the 'stop'th output
function slice(c, start, step, stop)
    local _
    return coroutine.wrap(function()
        for i = 1, start-1 do
            _ = c()
        end
        for i = start, stop do
            if (i - start) % step == 0 then
                coroutine.yield(c())
            else
                _ = c()
            end
        end
    end)
end

local alphabet = {}
for c = string.byte('a'), string.byte('z') do
    alphabet[#alphabet+1] = string.char(c)
end
-- only yields combinations 100 through 102
-- requires evaluating the first 100 combinations, but not the next 5311633
local s = slice(Combinations(alphabet, 10), 100, 1, 102)
for i in s do
    print(table.concat(i))
end
--> abcdefghpr
--> abcdefghps
--> abcdefghpt

協同程式可用於管道構造,如在 Lua 程式設計中所述。PiL 的作者 Roberto Ierusalimschy 也發表了一篇關於使用協程來實現更先進和一般流控制機制(如延續)的論文。