Squirrel その3

GWはツーリングに行く予定ですが、問題なのはガソリン代。
130円超えてますよ。アホかと。
バイクは車に比べると燃費はいいけど、それにしたってきついよ。

前回の続き。
Squirrel でも関数の作成が可能です。もちろん、引数も使えて戻り値も使えます。

function foo(a, b)
{
    return a + b;
}

引数、戻り値ともに型は指定できません。
整数や浮動小数点数を使う分には問題ないですが、テーブルや配列を使うときは気をつけてください。
この手のエラーはコンパイル時には出てこないで実行時に出てきます。
なお、整数等の引数はコピーになりますが、テーブルや配列は参照渡しになります。
関数は関数ポインタのように変数に代入することが出来ます。もちろん、その使用も可能です。
また、テーブル内の関数は以下のように登録できます。

local a = {};
function a::foo(a, b)
{
    return a + b;
}

スロット追加と同じようにも出来ますし、初期化時に登録することも可能です。
また、無名関数が使用できます。

local a = function(a, b)
{
    return a + b;
}

Luaクロージャとは違い、無名関数が定義されている関数内のローカル変数をいじることは出来ません。
ただし、以下のようにすることで外部変数を参照することが出来ます。

local x = 0;
local y = 0;
local a = function(a, b) : (x, y)
{
    return x + y + a + b;
}

外部参照した変数は定数と同じで変更することが出来ません。が、テーブルの場合は各スロットの値を変更することは出来ます。スロットの追加も出来ます。
関数は再帰呼び出しも可能です。スタックの使いすぎに注意しましょう。
次に説明するのはコルーチンです。聞きなれない単語と思う人もいるでしょう。
通常の関数はサブルーチンと呼ばれます。
サブルーチンはメインルーチン(もしくは別のサブルーチン)から呼び出され、サブルーチンが終了するとメインルーチンに戻ります。
コルーチンの場合、別のルーチンから呼び出されますが、終了する前に一時停止して元のルーチンに戻ることが可能です。
そして適当な場所で一時停止したコルーチンに戻ることが出来ます。もちろん、停止した場所にです。
このような方法は別スレッドを作成することでCでも使用できますが、マルチスレッドのように複数のスレッドが同時に実行されるわけではありません。
Squirrel では2つの方法でコルーチンを使用することが出来ます。
1つ目はジェネレータ関数です。

function gen_test(n)
{
    for(local i=0; i<n; i++){ yield i; }
    return null;
}

local gen = gen_test(10);
local x;
while(x = resume gen){ print(x + "\n"); }

関数ポインタと違い、引数を指定した関数を変数にぶち込むとジェネレータ関数になります。
yield 命令が発行されたところで一時停止します。この時、yield の後に指定された値を戻り値にします。
ジェネレータ関数を作成した段階でも停止しています。これを動かすには resume 命令を用います。一時停止したジェネレータ関数を再起動します。
なお、終了済みのジェネレータ関数を resume するとエラーになります。
もう1つはスレッドを用いる方法です。

function thread_test(n)
{
    local ret = 0;
    for(local i=0; i<n; i++){ ret = suspend(i + ret); }
    return -1;
}

local thr = newthread(thread_test);
local ret = thr.call(10);
do{
    print(ret + "\n");
    ret = thr.wakeup(ret);
} while(thr.getstatus() == "suspended");

newthread 命令でスレッドを作成します。スレッドの状態は getstatus 命令で取得でき、idle, running, suspended のいずれかの文字列が返ってきます。
suspend 命令でコルーチンを一時停止し、wakeup 命令で再起動します。
ジェネレータ関数と違う点はコルーチンの一時停止時以外に、再起動時にコルーチンの方に戻り値(というのも変な話だが)を渡すことができる点です。
suspend 命令の引数は call か wakeup の戻り値になり、wakeup 命令の引数は suspend の戻り値になります。