最近、vimの勝手スクリプトとかもluaで書いたりしていて、便利だなぁって思っています。
coroutineについてメモしておく。
coroutineの大体
coroutineは独自のluaスタックとローカル変数を持つスレッド的な物。
スレッド的だけれど、OSに制御が有るわけではなく、制御を明け渡すならロジック自体がそれを通知しないといけない。
create()するとコルーチンが作られる。resume()で実行/復帰、コルーチン内でyield()すると制御を戻せる。この時、戻り値として値をやり取りしたりもできる。
なんか適当に機能を使いまくると、こんな感じ。
local colo = coroutine.create(
function( init )
if init == nil then init = 0 end
local i = init
while i < 10 do
coroutine.yield(i)
i = i+1
end
assert( false )
return -1
end
)
repeat
local bStat, vRet = coroutine.resume( colo, 5 )
if bStat then
print("->", vRet )
else
print("assert! -> ", vRet)
end
until coroutine.status( colo ) == "dead"
-> 5
-> 6
-> 7
-> 8
-> 9
-> -1
coroutine.create()
coroutine.create( fn )
create()すると、新しいコルーチンが返される。fnは関数でなければならない。
この関数fnが、コルーチンが走行中に実行される関数になる。
coroutine.resume()
coroutine.resume( co, ... )
resume()するとコルーチンcoの実行を開始/再開できる。coに続いて付けた値で、コルーチンの関数fnの引数と出来る。(ただしもちろん開始の時だけ)
この関数の第一戻り値では、コルーチンがアサートで死んだかどうかが返される。このとき、親のスレッドは死なないので外側からハンドリングすることができる。
assert! -> coroutine1.lua:4: assertion failed!
以降は、関数 or yield()で戻された値が第二・第三・・・と帰っていく。
coroutine.status()
coroutine.status( coro )
status()するとスレッドの状態が返される。これらの値とその状態はこんな感じ。
値 | 意味 |
---|---|
suspend | コルーチンは新品、もしくはyield()で停止中。resume()可能。 |
normal | アクティブだが止まっている。自分の親。 |
running | 現在実行中。自分自身。 |
dead | return/assertで既に終了している。 |
coroutine.yield()
coroutine.yield( ... )
コルーチンの実行を中断する。次回resume()されたときは、yieldの次の行から実行される。ここで、引数に値を与えると呼び出し元のresume()の第二戻り値以降へ返すことができる。
当然だが、resume()されるまでスタックやローカル変数は保全されている。
coroutine.running()
coroutine.running()
走行中のコルーチンを返す。つまり自分自身。
local colo = coroutine.create(
function( )
print( coroutine.status(coroutine.running()))
return -1
end
)
repeat
coroutine.resume( colo, 0 )
until coroutine.status( colo ) == "dead"
running
coroutine.wrap()
coroutine.wrap(fn)
wrapはcreate()のひねくれた版で、関数的に呼び出すとresumeされるコルーチンを返す。ただし、このオブジェクトはthreadオブジェクトではなくてfunctionオブジェクトなので、一切にcoroutine.*系関数が使えない。また、通常のコルーチンではエラーは分離されていたけれど、wrap()で作ったコルーチンのエラーは親へ伝播する。
使い所はなかなかだが、場合によってはすっきり書ける。
local wcolo = coroutine.wrap(
function( i )
while true do
i = i + coroutine.yield()
print("->", i)
end
end
)
wcolo(1)
wcolo(2)
wcolo(3)
wcolo(4)
-> 3
-> 6
-> 10
再帰関数の出力を外でやる、みたいなことをやったりするのにも使える。