先に「ASSERT、例外処理」内にあるpcallの記述を見ておいたほうが、Luaにおける実装指針が理解できるので良い。
コルーチンはpythonのyieldなど、その他の言語でも良く出てくる概念である。
LUAのコルーチンはそこそこ(というかかなり)強力な非対称コルーチンと言える。
(非対称コルーチンにしているのは、結局その方がハンドリングが楽だからと思われる)最近のスクリプト言語では、通常コルーチンは実装されている。
しかし、その正確なコントロールは最終的には難しくなってしまうため、
使用には注意を要するし、安易な利用は避けるべきである。
特にゲームスクリプトでは、使用を禁止する方がトラブルが無くて良い。
あやふやな知識による使用は発見・除去双方が困難なバグを招く主原因となりやすい。
function dofile (filename) co = coroutine.create( function() print("hi") end ) print(co) --> thread: 000000000033F490 end
コルーチンは
suspended 中断 runnning 実行 dead 終了 normal 通常 の4つの状態を持つ。
createしただけでは、suspededの状態となっている
該当のコルーチンインスタンスの状態は
co = coroutine.create( function() print("hi") end ) ・・・ -- 何かの処理 coroutine.status(co)でわかる。
該当のコルーチンを実行するには
co = coroutine.create( function() print("hi") end ) ・・・ -- 何かの処理 coroutine.resume(co)でよい。
該当のコルーチンを一時的に停止して、後で続きから再開するような仕組みを入れるには
コルーチンの実行対象となる関数内に、次のように「coroutine.yield()」を入れる。co = coroutine.create( function () for i=1, 10 do print("co", i) coroutine.yield() end end ) coroutine.resume(co) ---> co 1 (coroutine.yield()まで実行され、suspendedに戻される) coroutine.resume(co) ---> co 2 (coroutine.yield()まで実行され、suspendedに戻される)コルーチンが実行に失敗した場合には、
print(coroutine.resume(co)) ---> false cannot resume dead coroutine というように第1返値にfalse, 第2返値にエラーメッセージとなる。resumeは保護モードで実行される。よってpcallと同様となる。
coroutineへの引数は、以下のようにそのまま、resumeに第2引数以下として渡す。
coroutine.yield()への引数がそのまま、resume呼び出しの2番目以降の返り値となる、
その次に呼び出す際の第2引数以降は、必要はない。co = coroutine.create( function(a, b) for i=1, 10 do print(a,b) coroutine.yield( a+5,b-5 ) end return 3,4 end ) coroutine.resume(co, 1, 2) ---> true, 6, -4 coroutine.resume(co) ---> true, 6, -4 coroutine.resume(co) ---> true, 6, -4 ・・・・ coroutine.resume(co) ---> true, 3, 4 coroutine.resume(co) ---> false, cannot resume dead coroutine
デザインパターンの一種
メッセージブロックを送信する側の処理と、メッセージブロックを受信して、実際に処理する処理の分離 データの具体的な入力手段と、実際の入力されたデータ処理を疎結合にするのが狙いである。function recieve (prod) local status, value = coroutine.resume(prod) return value end function send (x) coroutine.yield(x) end function producer () return coroutine.create(function () while true do local x = io.read() send(x) end end ) end function consumer(prod) while true do local x = recieve(prod) io.write(x, "\n") end end consumer(producer())
2ずつ増えるイタレータ
function itr() local co = coroutine.create(function () local i = 1 while true do coroutine.yield(i) i = i + 2 end end) return function () local state, res = coroutine.resume(co) return res end end for i in itr() do print(i) if i > 10 then break end end
より簡単に書くならば、coroutine.wrapを利用する。 ccoroutine.wrap は functionを渡すと、コルーチンを生成し、''
該当コルーチンのresume関数(自体)を返り値として渡す。local itr = coroutine.wrap(function () local i = 1 while true do coroutine.yield(i) i = i + 2 end end) for i in itr do print(i) if i > 10 then break end end
これらのfor .. in の挙動を理解するためには、
[イタレータ>lua_reference_watch_out_point_iterator]の項目の「for ... in の展開」を知っておく必要がある。