この文書の目的
関数を定義したあとに、その関数にて使用しているグローバル変数をローカルな変数に置きかえる方法を示します。具体的には、
count=0 -- グローバル変数
function glooba_inc()
count=count+1 -- グローバル変数 count に対して +1
print("count="..count)
end
という関数を、プログラムにより、以下のローカルな変数を使う関数へと変更します。
local count=0; function local_inc()
count=count+1 -- グローバル変数 count に対して +1
print("count="..count)
end
動作確認した Lua のバージョン
Lua-5.3.4。メタテーブルおよび load 関数を有する、最近の Lua であれば動作すると思います。
しくみ
load 関数を用いると、その関数(正確にはチャンク)の実行に使用する環境を指定できます。環境を差し替えることにより、関数中の変数が何を指すのかを、後から差し替えることができます(上の関数の例では、count という変数)。
load 関数では、チャンクとしてプログラムを格納した文字列もしくはコンパイル済みのバイナリ列(VM のバイトコード)を指定できます(バイナリ列の場合、第 3 引数(=mode)に "b" という文字列を指定する必要があります)。
関数のコンパイル済みのバイナリ列ですが、string.dump 関数を用いることにより得られます。本文章では、これらの関数を活用することにより、関数中のグローバル変数を、ローカル変数に差し替えることを実現します。
具体的な方法
一連のコードにより、具体的な方法を示します。
> -- global_inc() は既に定義してあるものとする
> globa_inc_bin=string.dump(globa_inc) -- VM のバイトコード列を取得
> localEnv={} -- ローカルな環境用のテーブル
> localEnv.count=100 -- 差し替えたい変数を初期値と共に定義
> setmetatable(localEnv,{__index=_ENV}) -- 差し替えたい変数以外は _ENV を使用
> local_inc=load(global_inc_bin,nil,"b",localEnv) -- global_inc から local_inc を生成
> count -- global_inc() で使用している変数の値を確認
0
> local_inc()
count=101
> local_inc()
count=102
> count -- global_inc() で使用している変数の値を確認(変化していない)
0
まとめ
コンパイル済みのバイナリ列(バイトコード)に変換することにより、実行時の環境を差し替えることが可能となりました。結果として、関数定義時にはグローバル変数として書かれていたコードを、ローカル変数を使用するものへと差し替えることができました。
上の例では global_inc() から local_inc() を生成した訳ですが、ローカルな環境を都度生成すれば、同様の方法で local_inc2() や local_inc3() など、それぞれ独立した変数を抱える関数を生成することもできます。
追記:
この方法を応用した「Lua で、self.name と書かなくても name だけでメンバ変数やメソッドにアクセスできるようにする方法(または self を使わないオブジェクト指向プログラミング)」を書きました。こちらもあわせてご覧になっていただければ幸いです。