Luaリファレンス 要注意点 ~コルーチン~

この項目を見る前に

先に「ASSERT、例外処理」内にあるpcallの記述を見ておいたほうが、Luaにおける実装指針が理解できるので良い。

コルーチン

コルーチンはpythonのyieldなど、その他の言語でも良く出てくる概念である。
LUAのコルーチンはそこそこ(というかかなり)強力な非対称コルーチンと言える。
(非対称コルーチンにしているのは、結局その方がハンドリングが楽だからと思われる)

最近のスクリプト言語では、通常コルーチンは実装されている。
しかし、その正確なコントロールは最終的には難しくなってしまうため、
使用には注意を要するし、安易な利用は避けるべきである。
特にゲームスクリプトでは、使用を禁止する方がトラブルが無くて良い。

あやふやな知識による使用は発見・除去双方が困難なバグを招く主原因となりやすい。

コルーチンの作成

coroutine.create(func_type)

function dofile (filename)
co = coroutine.create( function() print("hi") end )
print(co)     --> thread: 000000000033F490
end


コルーチンの状態

コルーチンは

suspended中断
runnning実行
dead終了
normal通常

の4つの状態を持つ。

createしただけでは、suspededの状態となっている

コルーチンの状態取得

coroutine.status(co)

該当のコルーチンインスタンスの状態は

co = coroutine.create( function() print("hi") end )
・・・ -- 何かの処理
coroutine.status(co)

でわかる。

既存のコルーチンの実行

coroutine.resume(co)

該当のコルーチンを実行するには

co = coroutine.create( function() print("hi") end )
・・・ -- 何かの処理
coroutine.resume(co)

でよい。

既存のコルーチンの一時停止(あとで続きから再開する場合)

coroutine.yield()

該当のコルーチンを一時的に停止して、後で続きから再開するような仕組みを入れるには
コルーチンの実行対象となる関数内に、次のように「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

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())

イタレータとコルーチン

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

より簡単に書くならば、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 の挙動を理解するためには、
イタレータの項目の「for ... in の展開」を知っておく必要がある。


トップ   差分 履歴 リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2016-01-27 (水) 00:00:00