resumeとyield
resume/yieldで呼び出し元とコルーチンの間でデータを交換します。
「resumeとyield」はCoddyKit上の無料Lua Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLua Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Lua Academyコースには全4レッスンが含まれています。
コルーチンへの値の渡し方
最初のcoroutine.resumeに渡した引数は、コルーチン本体の関数パラメーターになります。その後のresumeに渡した引数は、コルーチンを一時停止させたcoroutine.yield()呼び出しの戻り値になります。
local co = coroutine.create(function(a, b)
print("received:", a, b) -- 10 20
local c = coroutine.yield() -- pauses
print("after yield:", c) -- 30
end)
coroutine.resume(co, 10, 20) -- first call: passes a,b
coroutine.resume(co, 30) -- subsequent: passed to yieldyieldからの値
coroutine.yield(val1, val2)に渡した値は、yieldで一時停止したcoroutine.resume()呼び出しの戻り値になります。つまり、yieldは呼び出し元に値を「返す」ことができます。
local co = coroutine.create(function()
coroutine.yield(1, "hello")
coroutine.yield(2, "world")
return 3, "done"
end)
local ok, a, b = coroutine.resume(co)
print(ok, a, b) -- true 1 hello
ok, a, b = coroutine.resume(co)
print(ok, a, b) -- true 2 world
ok, a, b = coroutine.resume(co)
print(ok, a, b) -- true 3 done双方向通信
コルーチンを使うと、真の双方向通信が可能になります。呼び出し元がresume経由でデータを渡し、コルーチンがそれを処理して結果をyieldで返します。resumeとyieldの各組み合わせが、同期的なデータ交換になります。
local co = coroutine.create(function(x)
while true do
x = coroutine.yield(x * x) -- yield x^2, get next x
end
end)
coroutine.resume(co) -- start (x=nil initially)
local ok, sq
ok, sq = coroutine.resume(co, 3); print(sq) -- 9
ok, sq = coroutine.resume(co, 5); print(sq) -- 25
ok, sq = coroutine.resume(co, 7); print(sq) -- 49プロデューサーとコンシューマー
これはコルーチンの典型的なパターンです。プロデューサーは項目を1つずつyieldし、コンシューマーは次の項目が必要になるたびにプロデューサーをresumeします。どちらか一方が先行することがないため、完全に同期できます。
local function producer(items)
return coroutine.create(function()
for _, item in ipairs(items) do
coroutine.yield(item)
end
end)
end
local pro = producer({"apple","banana","cherry"})
-- Consumer drives the loop
while true do
local ok, item = coroutine.resume(pro)
if not ok or item == nil then break end
print("consumed:", item)
endコルーチンによるパイプライン
複数のコルーチンをパイプラインとして連結できます。各ステージは前のステージから読み取り、次のステージへyieldします。最後のステージをresumeしてパイプラインを駆動すると、その呼び出しがチェーンを通じて逆方向に伝播します。
local function source(items)
return coroutine.wrap(function()
for _, v in ipairs(items) do coroutine.yield(v) end
end)
end
local function filter(iter, pred)
return coroutine.wrap(function()
for v in iter do
if pred(v) then coroutine.yield(v) end
end
end)
end
local function map(iter, fn)
return coroutine.wrap(function()
for v in iter do coroutine.yield(fn(v)) end
end)
end
local pipeline = map(filter(source({1,2,3,4,5,6}),
function(n) return n%2==0 end),
function(n) return n*n end)
for v in pipeline do io.write(v.." ") end
print() -- 4 16 36非同期処理のシミュレーションにコルーチンを使う
OSスレッドがなくても、コルーチンで非同期処理をシミュレートできます。「待機中」はyieldし、結果の準備ができたらresumeします。スケジューラーが結果を渡してコルーチンをresumeします。
local scheduler = {}
local current = nil
local function await(key)
coroutine.yield(key) -- tell scheduler what we need
return coroutine.yield() -- wait for result
end
local co = coroutine.create(function()
local data = await("fetch:users")
print("Got users:", data)
end)
-- Start coroutine
local ok, want = coroutine.resume(co)
print("Coroutine wants:", want) -- fetch:users
-- Simulate delivering the result:
coroutine.resume(co, {{name="Alice"},{name="Bob"}})関数内でのyield
最上位の関数だけでなく、コルーチンから呼び出された任意の関数内でもyieldできます。yieldはすべての入れ子になった呼び出しを通ってresumeまで戻ります。これは「一時停止された呼び出しスタック」と呼ばれます。
local function yieldTwice(label)
coroutine.yield(label .. ":1")
coroutine.yield(label .. ":2")
end
local co = coroutine.create(function()
yieldTwice("A")
yieldTwice("B")
end)
for i = 1, 4 do
local ok, v = coroutine.resume(co)
print(v)
end
-- A:1, A:2, B:1, B:2コルーチンはスレッドではありません
LuaのコルーチンはOSスレッドではありません。一度に実行されるコルーチンは1つだけで、プリエンプションも並列性もありません。すべてのコルーチンが1つのOSスレッドを共有します。真の並列処理が必要な場合は、別々のOSスレッドで複数のLuaステートを使用する必要があります。
-- This is NOT parallel - interleaved execution
local function task(name, n)
for i = 1, n do
print(name, i)
coroutine.yield()
end
end
local t1 = coroutine.create(function() task("A", 3) end)
local t2 = coroutine.create(function() task("B", 3) end)
-- Must explicitly schedule:
for _ = 1, 3 do
coroutine.resume(t1)
coroutine.resume(t2)
end
-- A1, B1, A2, B2, A3, B3 (interleaved, NOT parallel)resume後の状態
コルーチンを再度resumeするかどうかを判断するには、状態を確認してください。実行を完了したコルーチン(本体からreturnしたコルーチン)は「dead」状態になり、resumeするとエラーとともにfalseを返します。ループ内でresumeする前には、必ず状態を確認してください。
local co = coroutine.create(function()
for i = 1, 3 do coroutine.yield(i) end
end)
while coroutine.status(co) ~= "dead" do
local ok, val = coroutine.resume(co)
if ok and val ~= nil then
print("val:", val)
end
end
-- val: 1, val: 2, val: 31回のresumeで複数回yieldすること
coroutine.resumeを1回呼び出すと、次のyieldまで、または終了するまでコルーチンが実行されます。1回のresumeでコルーチンを複数回yieldさせることはできません。yieldするたびに一時停止し、次のresumeを待機するためです。
local co = coroutine.create(function()
local x = coroutine.yield("first")
print("got:", x)
local y = coroutine.yield("second")
print("got:", y)
end)
local _, v1 = coroutine.resume(co) -- starts, yields "first"
print("yielded:", v1) -- first
local _, v2 = coroutine.resume(co, "a") -- passes "a", yields "second"
print("yielded:", v2) -- second
coroutine.resume(co, "b") -- passes "b", coroutine endsクイックチェック
coroutine.yield(v1, v2)に渡した値は、何になりますか?
復習:resumeとyield
まとめ:
- 最初のresumeの引数 → コルーチン本体のパラメーター
- その後のresumeの引数 → 直前のyield()の戻り値
- yieldの引数 → yieldを発生させたresume()の戻り値
- 双方向通信:各resume/yieldが1回のデータ交換
- 入れ子になった任意の関数からyield可能
- コルーチンは協調的であり、並列ではない
よくある質問
「resumeとyield」レッスンは無料ですか?
はい。「resumeとyield」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Lua Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Lua Academyコースには全4レッスンが含まれています。
「resumeとyield」で何を学びますか?
resume/yieldで呼び出し元とコルーチンの間でデータを交換します。 ブラウザで直接実行するハンズオンコードでLua Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Lua Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのLua Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「resumeとyield」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このLua Academyレッスンでコードを書いて実行できますか?
はい。すべてのLua Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- コルーチンの作成
- resumeとyield
- コルーチンの状態
- プロデューサー・コンシューマーパターン