0Pricing
Lua Academy · レッスン

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 yield

yieldからの値

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: 3

1回の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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. コルーチンの作成
  2. resumeとyield
  3. コルーチンの状態
  4. プロデューサー・コンシューマーパターン
← Lua Academyに戻る