*Luaリファレンス 要注意点 ~コルーチン~ [#wf540f3c] **この項目を見る前に [#u348e12b] >先に「[[ASSERT、例外処理>lua_reference_watch_out_point_except]]」内にあるpcallの記述を見ておいたほうが、Luaにおける実装指針が理解できるので良い。 **コルーチン [#k1e1a027] > コルーチンはpythonのyieldなど、その他の言語でも良く出てくる概念である。~ LUAのコルーチンはそこそこ(というかかなり)強力な非対称コルーチンと言える。~ (非対称コルーチンにしているのは、結局その方がハンドリングが楽だからと思われる)~ > 最近のスクリプト言語では、通常コルーチンは実装されている。~ しかし、その正確なコントロールは最終的には難しくなってしまうため、~ 使用には注意を要するし、安易な利用は避けるべきである。~ 特にゲームスクリプトでは、使用を禁止する方がトラブルが無くて良い。~ ~ あやふやな知識による使用は''発見・除去双方が困難なバグを招く主原因''となりやすい。 ~ **コルーチンの作成 [#l931ba2b] ***coroutine.create(func_type) [#jfd914b7] > #sh(lua){{ function dofile (filename) co = coroutine.create( function() print("hi") end ) print(co) --> thread: 000000000033F490 end }} ~ **コルーチンの状態 [#f7109bff] >コルーチンは |''suspended''|''中断''| |''runnning''|''実行''| |''dead''|''終了''| |''norma''l|''通常''| の4つの状態を持つ。~ ~ ''createしただけでは、suspededの状態''となっている **コルーチンの状態取得 [#u492f72a] ***coroutine.status(co) [#p17b6da2] >該当のコルーチンインスタンスの状態は #sh(lua){{ co = coroutine.create( function() print("hi") end ) ・・・ -- 何かの処理 coroutine.status(co) }} でわかる。 **既存のコルーチンの実行 [#vc4ad650] ***coroutine.resume(co) [#tc273227] >該当のコルーチンを実行するには #sh(lua){{ co = coroutine.create( function() print("hi") end ) ・・・ -- 何かの処理 coroutine.resume(co) }} でよい。 **既存のコルーチンの一時停止(あとで続きから再開する場合) [#r1b74dc4] ***coroutine.yield() [#u57814e8] >該当のコルーチンを一時的に停止して、後で続きから再開するような仕組みを入れるには~ コルーチンの実行対象となる関数内に、次のように「''coroutine.yield()''」を入れる。 #sh(lua){{ 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に戻される) }} >~ コルーチンが実行に失敗した場合には、~ #sh(lua){{ print(coroutine.resume(co)) ---> false cannot resume dead coroutine というように第1返値にfalse, 第2返値にエラーメッセージとなる。 }} resumeは''保護モード''で実行される。よってpcallと同様となる。 **コルーチンへと渡す引数 [#c304aaf0] >coroutineへの引数は、以下のようにそのまま、resumeに第2引数以下として渡す。~ coroutine.yield()への引数がそのまま、resume呼び出しの2番目以降の返り値となる、~ ''その次に呼び出す際の第2引数以降は、必要はない。'' #sh(lua){{ 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 }} **LUAでの、プロデューサ/コンシューマ [#o25f24a2] >デザインパターンの一種~ メッセージブロックを送信する側の処理と、メッセージブロックを受信して、実際に処理する処理の分離 データの具体的な入力手段と、実際の入力されたデータ処理を疎結合にするのが狙いである。 #sh(lua){{ 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()) }} **イタレータとコルーチン [#t561f17d] >2ずつ増えるイタレータ #sh(lua){{ 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 [#k146f4ff] >より簡単に書くならば、coroutine.wrapを利用する。 ccoroutine.wrap は functionを渡すと、コルーチンを生成し、''~ ''該当コルーチンのresume関数(自体)を返り値として渡す。''~ #sh(lua){{ 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 の展開」を知っておく必要がある。 [[イタレータ>lua_reference_watch_out_point_iterator]]の項目の「for ... in の展開」を知っておく必要がある。