\n
[ トップページ ]
[ ___ _CommandPrompt ]
[ ___ _JScript ]
[ ___ _MySQL ]
[ ___ _Cygwin ]
[ ___ _Java ]
[ ___ _Emacs ]
[ ___ _Make ]
[ ___ _Perl ]
[ ___ _Python ]
[ ___ _OpenGL ]
[ ___ _C# ]
[ ___ _StyleSheet ]
[ ___ _C++ ]
[ ___ _Winsock ]
[ ___ _Thread ]
[ ___ _VisualStudio ]
[ ___ _C ]
[ ___ _Win32API ]
[ ___ _Lua ]
[ ___ _PhotoShop ]
ヘッダ検索
■ 制御構文
■ if
SYNTAX
if 式 then 文 else 文 end
if 式 then 文 elseif 式 then 文 else 文 end
WARNING
型が異なるもの同士の比較はすべて false;
[ "false" | "nil" ] 以外はすべて 真
-- 以下すべて真
if ( 0 ) then ... end;
if ( "" ) then ... end;
if ( {} ) then ... end;
0 = "0" ; // RET : false
a, b = 128, 18;
if a == b then
print("equal" );
else
print("not equal" );
end
■ for
SYNTAX
for Name `=´ exp `,´ exp [`,´ exp] do block end
for 変数 = 初期値, 終了値[, 増加量] do 文 end
for 変数 = 初期値, 終了値, 増加量 do ~ end
0 から 10 まで
for i=0,10 do
print(i);
end
2 個飛ばしで
for i=0,10,2 do
print(i);
end
配列(テーブル)をイテレートする
a = { 3, 4, 5 }
for i=1,#a do
print( a[i] );
end
WARNING
[ i ]は ローカル変数のため, for 文が終了した瞬間に終了。
break については block 末尾のみできる
for i = 0, 10 do
print("test" )
do
break;
end
print("test" )
end
■ repeat
SYNTAX
repeat 文 until 条件式
local cnt = 0;
repeat
print( cnt );
cnt = cnt + 1;
until cnt > 3
■ while
SYNTAX
while 評価式 do 文 end
WARNING
break; は block 末尾にある必要がある.
■ foreach
SYNTAX
for key,value in pairs(連想配列) do 文 end
DESC
配列扱いのテーブルを巡回する場合は、ipairs() 関数を利用する。
local a = { 1, 2, 5, 8 };
for i, v in pairs( a ) do
print(tostring(i) .. " = " .. tostring(v) )
end
WARNING
巡回する index の最大値は最大のインデックスまで。
index は連続している必要はない。
■ 変数
Lua には 3種類の変数がある。
データ( 情報 )はすべて, テーブルという構造に格納される。
グローバル変数はグローバル用のテーブルにセットされる。
-- local と言わなければ, global
v = 10;
-- local 宣言で local変数扱い
local t = "test" ;
-- 値が代入されない変数は nil がセットされる。
local v;
POINT
変数に型はなく、任意の型のオブジェクトの場所だけを知っている存在。
動的にわりあてることができるが、
変数の型チェックをしてくれないため自前でする必要がある。
( Perl, Python 同様 )
静的わりつけ
Merit
実行前に チェック 可能
効率のよい cmp 作成可能
DeMerit
頻繁な型変換
param が異なる場合に複数の関数が必要.
動的わりつけ
Merit
Object の型をしらべて, 処理をするという動的な処理が可能
DeMerit
実効時にエラーが発生
param が異なる場合に複数の関数が必要.
ひとつの 変数(Symbol) に 文字列, 数値 を代入できる。
Java, C では変数に型情報があるため, 実行前にチェックができる。
スペルミスが素通りされる危険がある
■ ローカル変数
ローカル変数を定義するには local 文を使用する。
local i = 10;
local i, j = 10, 20;
SYNTAX
local name [= expression]
local namelist [= expressionlist]
POINT
ブロック内で local 文を使用する度に, 新たに変数が定義される。 ( C 同様 )
C と同じでブロック内のスコープになる。
i = 20 -- グローバル変数 i
do
local i = 10 -- 変数 i
do
local i = 7 -- これは別オブジェクトの 変数 i
-- 7
print (i)
end -- もっとも内側のブロックで変数は消滅する
-- 10
print (i)
end
for i=1, 10 do
local y; -- ここで新規定義
if y == nil then
y = 0
else
y = y + 1
end
done
■ グローバル変数
すべてのグローバル変数は、Luaの環境テーブルというテーブル内のフィールドとして存在する。
global変数とテーブルフィールドへアクセスする効果はメタテーブルによって変えられる.
各関数はそれぞれ独自に環境への参照を持つ.
関数が作られたとき、関数は、それが作られた関数から見える環境を受け継ぐ( ENV 同様
x == _env.x == gettable_event(_env, "x" )
( MetaMethod を利用する )
Lua で グローバル変数を宣言( 代入 )する際は,
TblGbl( _G ) に格納される
mt = {};
mt.__newindex = function( t, n, v );
POINT
block do ... end に属さない local v は file scope になる
保守性のために, local にすること.
-- 未定義変数のチェック
if ( v ~= nil )
end
■ 演算子
算術演算子
関係演算子
論理演算子( C などの 論理演算子と異なるので注意 )
■ or.and.not
if a == 10 and s == "test" then
print( "test" );
end
if a == 10 or s == "test" then
print( "test" );
end
if ( not a ) then
end
POINT
-- or : 最初の引数が false か nil 以外ならその値を返す
a = a or 10;
連結演算子 | 文字列連結
■ 代入
多重代入OK (右辺の値, 左辺の添え字がすべて評価された後, 代入 )
POINT
x, y = y, x; // 入れ替え可能.
関数も複数返すことが可能.
function f()
return 1, 2;
end
print( f );
■ 論理
if ( "aaa" ~= "bbb" )
print ( "diff" )
end
if ( "aaa" == "bbb" )
print ( "same" )
end
■ TableConstructor
SYNTAX
TableConstructor ::= { [fieldlist] }
Lua にも Constructor がある
tbl{ f1=val, f2=val ... } < -> tbl( {...} ) // Tbl関数に Arg として渡した関数.
-> DefalutArg の修理とか可能.
a = { [f(1)] = g; "x" , "y" ; x = 1, f(x), [30] = 23; 45 }
[exp1] = exp2 形式のフィールド
name = exp 形式のフィールドは ["name" ] = exp と等価
do
local t = {}
t[f(1)] = g
t[1] = "x" -- 最初のexp
t[2] = "y" -- 2番目のexp
t.x = 1 -- temp["x" ] = 1
t[3] = f(x) -- 3番目のexp
t[30] = 23
t[4] = 45 -- 4番目のexp
a = t
end
■ 関数呼び出し
( lisp と同様の設計. -> lua_type() ->
functioncall ::=
prefixexp args...
POINT
prefixexp と args が評価される ---> prefixexp が関数なら 与えられた引数で関数 call
ret = func( arg1, arg2, ...)
Function は
[ 何かの入力をうけとり ] ---> [ 何かの( 処理 | 判断 | 保存 )をして ] --> [ 何かを出力する ]
[ 何か ]を { int, float, vec3, string } と種類を限定することも可能.
Ret がない場合は nil をかえす
■ method として扱う
( OO 的な呼び出しをする. )
POINT
functioncall ::= prefixexp `:´ Name args
v:name(...) == v.name( v, ... ); == [ method ]
t:f(x) という表現は t.f(t,x);
終端呼び出し( 呼び出し側の stack entry を再利用する )
return functioncall のような呼び出し形式は
■ テーブル(Table)
Lua にはデータを構造化するのはテーブルのみ。
キーと値の組の集合からなる、連想配列のこと。
-- table の生成
tbl = { name="xxx" , age=10, price=0 }
-- テーブルへのメンバのアクセスには [] を使う。
-- .key の表記は [ "key" ] と同じ。
tbl[ "key" ]
tbl.key
-- 指定したフィールドがない場合は nil が返る
tbl.foo;
-- field の追加
tbl.foo = "bar" ;
WARNING
tbl.name 表記は tbl[ "name" ] と同じ。
次の処理は思ったとおりの動作をしない。
function isKeyExist( tbl, key )
if ( tbl.key !~ nil ) then
end
end
tbl.key は tbl[ "key" ] とみなされる。
そこで [ key ] とすれば正しく動作する。
if ( tbl[key] !~ nil ) then
end
■ 追加.削除
tbl[ "age" ] = 10
tbl[ "name" ] = "test"
-- キーは数値でも指定できる
tbl[ 1 ] = 10;
-- 削除をするには nil をセットする
tbl[ "name" ] = nil
-- iterate すると削減されたことがわかる
for key, val in pairs( tbl ) do
print ( key )
print ( val )
end
■ ソート(sort)
SYNTAX
table.sort( array [, function ] )
DESC
配列を昇順にソートする。
local a = {7, 5, 3};
table.sort(a);
-- 結果を表示
for i=1, #a do
print(a[i]);
end
比較用の関数を指定する。
a = {}
a[1] = { name="a" , price=300 };
a[2] = { name="b" , price=100 };
a[3] = { name="c" , price=200 };
function cmpfunc( a, b )
return a.price < b.price
end
table.sort( a, cmpfunc );
for i=1, #a do
print( a[i].price );
end
■ foreach(イテレート)
for t, val in pairs( members ) do
for k, v in pairs(val) do
print( k, v );
end
end
while で利用する
k, v = next( player1 );
while (k) do
print( v );
k, v = next( player1, k );
end
■ 配列(Array)
■ 配列
POINT
配列はテーブルのキーを数値にすることで利用する。
テーブルの特殊系にすぎない。
. Table{ Arr{} }
-- Constructor 起動( この処理は必須 )
a = {};
-- list 表記
-- 初期化リストつき
local a = { "x" , "y" , "z" };
print( a[1] );
数値 index として扱う
a = {}
-- 追加する
a[ #a + 1 ] = "a"
a[ #a + 1 ] = "b"
-- イテレートする
for i = 1, #a do
local s = a[ i ]
print( s )
end
配列の要素数をとるには 単項演算子( # ), table.getn(), table.maxn() をつかう
-- 3
a = { 2, 5, 6 }
print( #a )
-- 3
a = { "a" , "b" , "c" }
table.getn( a );
-- 3
a = { "a" , "b" , "c" }
table.maxn( a );
WARNING
テーブル内の最大の数値インデックス値を返す。
インデックスに数値が使われていない場合は 0 が返る
-- 0
local a = { a=1, b=2 }
print( table.maxn( a ) )
print( table.getn( a ) )
print( #a ) )
maxn はテーブル内の最大の数値インデックス値を返す。
#, getn() は連続している最大の数値インデックス値を返す。
local a = {}
a[1] = 0;
a[2] = 0;
a[100] = 0;
-- 2
print( #a )
print( table.getn( a ) )
-- 100
print( table.maxn( a ) )
a[ "key" ] = 10; // 文字 KEY による代入
a[ 3 ] = 20; // 数値を使用すれば, 配列としても可能
b = a; // 参照.
print(b.x) // == b["x" ]; BAD b.0
b.y = "test" ;
print( b.y );
// index 1 の値を削除.
b[3] = nil;
POINT
開始 idx == 1
要素数は可変.
使う前の定義が必要 -> arr = {};
空要素を取得しようとすると, nil が返る。
-- 疎行列
-- ( 配列index(索引)は連続している必要はない )
a[1] = 0;
a[2] = 0;
a[4] = 0;
a[5] = 0;
print( #a ) = 2;
変数と同じで、値のないインデックスを参照すると nil が返る。
a = { "x" , "y" , "z" };
-- nil
print( a[4] );
■ 追加
table.insert() でテーブルの末尾のインデックスに追加する。
テーブルの添え字は数値インデックスになる。
tbl = {}
-- index 1 に追加
table.insert(tbl, "A" )
-- index 2
table.insert(tbl, "B" )
-- index3
table.insert(tbl, "C" )
index を指定した場合は、その位置に追加されて残りは後ろにシフトされる。
-- D, A, B, C
table.insert(tbl, 1, "D" )
■ 削除
SYNTAX
table.remove( table, number )
local tbl = {}
table.insert(tbl, "foo" )
table.insert(tbl, "bar" )
-- 要素ではなく index を指定する
table.remove( tbl, "foo" );
local tbl = { 2, 4, 6 }
table.remove( tbl, 1 );
print ( #tbl )
-- イテレートする。
for i=1, #a, 1 do
print(a[i])
end
table.foreach( t, func ); -- call func( idx, val );
WARNING
間違った field を指定した場合は, nil がかえるので, spellmiss が素通りする可能性あり.
■ 結合
SYNTAX
table.concat (table [, sep [, i [, j )
要素を指定した文字列で結合して返す
a = {1,2,3}
-- 1__2__3
table.concat( a, "__" )
結合する要素の数を指定する
-- 1__2
table.concat( a, "__" , 1, 2 )
■ 2次元配列
1次元配列を利用して, 2次元配列を作成する.
配列の要素を配列にする。
m = {};
m[1] = {};
m[2] = {};
m[1][1] = 10;
print( m[1][1] );
■ Lua 言語の字句の構成
Lua が使用する, 内部 global変数. _VERSION
■ 値と型
[ 変数は型をもたず ], [ 値が型をもつ ] -> つまり 型自身はある.
変数には[ 宣言 ]は不要. -> 変数には型という概念がないから.
代入, 引数わたし, 関数の返値は すべて [ 参照 ]
■ 構文
■ comment
-- comment
WARNING
--- これはコメントではない
■ 日本語対応
Manual では Unicode , UTF8 が対応とのこと.
source code が S-JIS, Unicode で記述されていても, そのまま binary 列として渡される.
SJIS を利用する場合は, [[日本語]] とすること. ( \ をEscape しない. )
WARNING
さらにいうと, 環境に font がない場合もある.
■ 文字code
[ 文字という情報 ]を数値で表現する.
それを 文字Code( 符号 ) という
string.byte( s, i[, j] );
string.char( s, 1, 3 );
■ 文(Statement)
LuaProgram の実行単位のこと
文の区切りは ; または改行コード
-- 代入文
price = 40
price = 40;
■ chunk
SYNTAX
{stat [';']}
文の集合のこと。
Luaの実行の単位は chunk という
Luaはチャンクを、可変個の引数を持つ無名関数の本体として扱う。
チャンクはバイナリ形式のコンパイル済みコードであっても良い
改行code == 空白
var = 40;
■ ブロック
SYNTAX
do block end
POINT
明示的なブロックは変数宣言スコープをコントロールするのに便利
C 同様, 外側の block での宣言した変数は有効になる。
do
local j = j or 10;
print( j );
do
local k = 20;
print( j, k ); -- 10 20
end
print( j, k ); -- 10 nil
end
print( j ); -- nil
■ 代入
POINT
Luaは多重代入を許している
{ 左辺: 変数リスト }, { 右辺:式リスト}
i, a[i] = i+1, 20 -- 多重代入. ( 先ずすべての式を評価する -> 最後に代入 )
■ String(文字列)
文字列型は "" , '' で囲む
local s = "test"
local s = 'test'
-- 複数行にまたがるには [[ ]] でくくる
local s = [[message
test]]
■ 連結
■ 長さ
# でバイト数を返す
-- 3
print ( #"aaa" )
-- 6( SJIS )
print ( #"あいう" )
-- 9( utf8 )
print ( #"あいう" )
-- 3
string.len( "abc" )
-- 9( utf8 )
string.len( "あいう" )
■ 変換
"test" .. tostring( 1 )
-- 暗黙的に変換される
"test" .. 1
-- Escape 文字使用可能
"foo\n" | 'bar\t'
[[c:\foo\bar]]
[[c:\foo
\bar]]
■ 分割(split)
SYNTAX
string.gmatch( string, pattern )
DESC
split がないため gmatch で代用する。
gmatch はマッチしたすべての要素を順に返すイテレータ関数を返すため
for - in と併用して文字列を分割する。
local s = "d:/foo/bar/goo.txt" ;
local a = {}
for w in string.gmatch(s, "[^/]+" ) do
table.insert(a, w);
end
for i=1, #a, 1 do
print ( i .. " = " .. (a[i]) )
end
■ 部分文字列
string.sub( string, s, e )
local ret = string.sub( "aaa___bbb" , 1, 3 );
-- 7番目より後ろ
local ret = string.sub( "aaa___bbb" , 7 );
print( ret );
■ match(検索)
SYNTAX
string.match( string, pattern [, index ] )
DESC
pattern にマッチする文字列を string から返す。
index が指定された場合は index の位置から検索をする。
-- RET hello
local ret = string.match("hello world" , "%w+" )
-- RET f
string.match( "foobar" , "[a-z]" );
■ find(インデックス検索)
SYNTAX
string.find( string, regexp [, index ] )
RET
start, end : マッチした文字列の開始, 終了位置インデックス
nil : マッチしない場合
DESC
regexp にマッチする文字列を string から検索して
開始、終了の index を返す。
index が指定された場合は index の位置から検索をする。
-- 1, 4
s, e = string.find( "test" , "%a*" );
-- 4, 6
local s, e = string.find ( "aaa___bbb" , "___" )
-- 拡張子を返す
path = "d:/foo/test.txt"
local idx = string.find( path, '%.' )
local ext = string.sub( path, 0, idx - 1 )
-- 1, 3
local path = "c:/foo.txt"
local s, e = string.find( path, "[Cc]:/" );
string.upper();
string.lower();
string string.format("string %s" , s)
%-12s : 左寄せ.
%+12d : [+-] 記号.
string.gsub( s, ptn, d[, n] )
s = string.gsub( "foo" , "[a-z]" , "c" );
string.len( s );
string.rep( s, n ); -- repeat
■ 反転
-- "cba"
string.reverse( "abc" )
■ include(require)
-- 同一ディレクトリの lib.lua をロードする。
require "lib"
add(1, 2);
呼ばれる側
function add( a, b )
return a + b;
end
module (name [, ・・・])
モジュールをつくる。
作成されるのはテーブルであり、 package.loaded[ test ] というテーブルとして作成される。
または global テーブルに追加される。
またこのテーブルをカレントの関数の環境にする。
パッケージ名は _NAME 属性に設定される。
名前は Python のモジュールと同じくネストすることもできる。
require (modname)
指定したモジュールをロードする。
package.loaded テーブルをまずはロードすみか検索する。
そうならば package.loaded[modname] を返す。
ローダが見つかれば、
C ローダのモジュールの検索先は package.loaders 配列を変更することで制御できる。
package.path を検索する。
C ローダが見つかると, modname を使ってコールをしてそれをpackage.loaded[modname] にセットする。
つまりロード先を C で実装できる。
■ Moduleの定義
SYNTAX
module( "name" , package.seeall )
require( "name" , package.seeall )
DESC
file を module として扱うように宣言.
-- module 名がそのまま NameSpace || tbl ? になる.
require( "test" );
test.sum( 100, 16 );
モジュールの定義は
定義する側のファイルの頭でモジュール名を引数に module 関数を実行します。
以後、module 関数の引数で指定した名前の テーブルに環境が変更される
その副作用として
グローバル環境で定義されている関数の呼び出し (組み込み関数の math.random() 等も含む)
もできなくなってしまいます。
そのため、module 関数の引数の 2 番目に package.seeall を指定することで
この対処が可能になっている
-- ファイル mod_test.lua
module("mod_test" ,package.seeall)
value=2
function box_muller()
local alpha=math.random() -- package.seeall のおかげで使える
local beta =math.random()
return math.sqrt(-2*math.log(alpha))*math
.sin(2*math.pi*beta)
end
■ 関数(Function)
SYNTAX
function ::=
function funcbody
funcbody ::=
`(´ [parlist1] `)´ block end
function 関数名( [引数list] ) 文 end
function func(x)
print(x)
return 1;
end
local func = function(x)
print(x)
return 1;
end
local ret = func( 10 );
print ( ret );
POINT
Lua では全ての関数は値になる。 (関数もファーストクラスオブジェクト)
関数定義も命令のひとつ.
Host から呼ぶことも, Lua 自身から呼ぶことも可能
関数定義は実行可能な式. 関数型の値を持つ
関数が呼ばれると、実引数リストは仮引数リストの長さに調節される
( 多ければ,きりすてられる, たりなければ, nil がセット )
POINT
関数定義も、[ 関数名の名前を持つ変数 ]に [ 関数の実体を代入 ]していることになる。
add = function(x, y) return x*y end
add(x, y);
■ 関数呼び出し
POINT
複数の値をかえす
f() -- 戻り値は0個に調節される
g(f(), x) -- f()の戻り値は1個に調節される
return f() -- f()の戻り値をすべて返す
{f()} -- f()のすべての戻り値からなるリストを作る
■ 可変引数
SYNTAX
[ function(...) ]
function ( a, b, ... )
a = select(5, ...); -- 可変引数 5 番目取得.
end
string.format
function test(...)
local arr = {...};
for i=1, #arr do
print( arr[i] );
end
end
do
test( "a" , "b" , "c" , 10 );
end
function fnMyUpper( s )
local l = string.len( s );
local str = string.upper( s );
return l, str;
end
do
local len, s;
len, s = fnMyUpper( "aiueo" );
print( len.."\t" ..s);
end
■ 複数の値を返す関数
( Stack を利用することで実現している
Lua は複数の値を返すことができる。
関数は data を返す際に, push
代入する際に pop;
x, y = foo();
-- 実は pop している.
POINT
次の実行文では, stack 内容は消去される.
x, y = 1, 2; も同様
( stack に 1, 2 を push してから x, y へ pop )
だから次のことができる。
■ return
SYNTAX
return [ expression ]
DESC
関数やチャンク (関数) から値を返すために使う
POINT
return, break は block の最後で利用できる。
function t.a.b.c:f (...) ... end
t.a.b.c.f = function (self, ...) ... end
POINT
[ メソッド ]とは、暗黙の引数 self を余分に持つ[ 関数 ]
■ ラムダ関数(名前なし関数)
方針としては [ その場で 1 度しか利用しない関数 ]は, 名前なし関数にする.
登録済みの関数は 未使用になったら GC で回収される.
a = {}
a[1] = { name="a" , price=300 };
a[2] = { name="b" , price=100 };
a[3] = { name="c" , price=200 };
table.sort( a, function ( a, b ) return a.price > b.price end );
for i=1, #a do
print( a[i].price );
end
■ 文としての関数呼び出し
( 副作用を許すため, 関数呼び出しは文として実行可能 )
stat ::= functioncall // 戻り値はすべて捨てられる.
Lua は Luaで書かれた関数もCで書かれた関数も呼べる
■ Error 処理
POINT
error 関数は Lua interpreter を[ 終了 ]させる
スクリプト内で error を利用することで
明示的に Error を発生させる.
Lua は組み込み拡張言語であるから, Lua の処理は Host 側からスタート。
Error が発生すると, 制御が C に返る。
■ pcall
SYNTAX
pcall( function, arg1, ... )
RET
true :
false : エラーが発生
-- エラー制御
function test( s )
print(s);
error("Err" );
end
if ( pcall( test, "foo" ) )then
print("OK " );
else
print("error" );
end
■ metatable
metatable とは
userdata, table への演算に対してどの関数を呼び出すのかを
連想配列で表した表( テーブル )のこと。
データ型はテーブルと同じで、userdata, table と関連づけるだけの違い。
■ MetaMethod とは演算子を独自処理におきかえる
POINT
[ 演算子 ]を独自処理におきかえる.( operator のこと )
MetaMethod を利用することで実現している.
tbl の値取得, 関数呼び出しの値を変更することも可能.
OO では [ 演算子を独自に定義できる ]ことが特徴.
{"pos" , getPos() } < -> tbl 経由で呼び出す. ?
{ data, Evt=Func } の組を MetaTbl として にセットすることで Lua に通知する。
■ event の種類
-- MetaMethod
local data1 = { name="a" , price=120}
local data2 = { name="b" , price=100}
function add( op1, op2 )
return op1.price + op2.price;
end
-- data1 の加算 + には myAdd を利用するように登録
setmetatable( data1, { __add = add, ... } )
print( data1 + data2 );
■ Overload
POINT
metatable はある値( Object )に対して, 特別な演算を overload する際に便利。
スレッド、関数、ユーザーデータ型のオブジェクトはメタテーブルをもつ。
{ KEY == Event , 値 = MetaMethod }
使いどころ
その値に対して特殊な処理をした際の, 挙動を定義する
数値以外の値が加算のオペランドになった ---> Luaはメタテーブルの "__add" フィールドをチェック
加算を行うためにその関数をcall
Object のふるまいを定義する
metatable は、算術演算、関係比較、連結、長さ演算子、インデックス付けについて
オブジェクトがどう振る舞うかを制御する
メタテーブルがコントロールできる演算を以下のとおり
各演算のキーは、その名前の前に2つのアンダースコア `__´ が付く文字列
__add, a + b の時にコールされる
__sub,
__mul,
__div,
__mod,
__concat,
__len
__tostring print( a );
-- 配列に metatable を セットして, add を overload してみる
setmetatable( metatableをセットする値, {metatable}),
metatable = {_add, 関数名 }
■ 環境table
環境は table のひとつ.
thread に関連つけられた環境は, global環境と呼ぶ.
■ Class
■ クラスの定義
POINT
Lua は Table を利用して Object をあらわす。
構造体としてとらえることも可能
Method の実体は Table の Field の値として関数があるだけ。
Lua Table を利用することで, Object を作成できる。 { data , method ... }
キーをメンバ変数名とみなせば、構造体としても表現できる。
Method には InstanceData をわたしたい.
file.open( file ) ---> file:open(); となる.
-- MetaTable
Colormeta = {
-- + の演算はこの関数でオーバーライド
__add = function(a, b)
local r, g, b = (a.r+b.r), (a.g+b.g), (a.b+b.b)
return newrgb( r, g, b );
end,
-- 文字列表現
__tostring = function(a)
return string.format("rgb %3d %3d %3d" , a.r, a.g, a.b )
end,
}
-- ctor
function Color( r, g, b )
local tbl = { r = r or 0, g = g or 0, b = b or 0 };
-- 毎度設定しないとだめ
setmetatable( tbl, Colormeta );
return tbl;
end
-- テーブルを作成して返す
g = Color(0, 255, 0);
b = Color(0, 0, 255);
-- MetaMethod を使う。
cyn = g + b;
print( cyn );
function T.new(value)
--
local obj = {
value = value,
toString = function(self)
return "T: value=" .. tostring(value)
end
}
return obj
end
-- new clsFoo(); の模倣.
obj = T.new(5)
print( obj:toString() )
obj2 = T.new(7)
print( obj2:toString() )
■ メソッドの定義
POINT
: を記述することで, メソッドのような記述ができる。
obj:function() とすると, 第一引数に暗黙のパラメータとして obj 自身がわたる。
実体は obj.function( obj ); と同じ。
( 構造体もある意味で 連想 Table -> obj.key = val )
String = {
-- メンバ変数
val = 3,
-- { KEY , 関数body } でメソッドらしくする。
-- . の代わりに : で代用する。
toString = function(self)
return "value=" .. tostring( self.val )
end
}
次のように使う
String:toString();
-- これと同じ
String.toString( self );
[ : ] を利用した関数定義
function object:read( a, b, c )
-- self は local 変数
print( self );
end
function object.read( self, a, b, c )
-- self は local 変数
print( self );
end
新規の型を定義できないため、クラスのインスタンスを作成するには
コンストラクタに相当する関数内でテーブルを作成して返す。
function Vector( x, y, z )
-- インスタンスに相当するテーブルを作成
local tbl = { x=x, y=y, z=z }
-- メソッドを定義
tbl.length = function( self )
return math.sqrt( self.x*self.x + self.y*self.y + self.z*self.z )
end
tbl.normalize = function( self )
local n = self:length()
self.x = self.x / n
self.y = self.y / n
self.z = self.z / n
end
return tbl
end
-- インスタンスを作成
local v = Vector( 1, 2, 3 )
-- メソッドをよびだす
v:normalize()
local len = v:length()
print ( len )
■ Closure
source code 上のまとまりのある単体.
Cの関数を作成するとき、いくつかの値を関連付けて Cのクロージャ を作ることができる。
( 関数に変数を付属させたもの関数のこと )
( 関数内で使用するdataを関数内にとじこめる )
Table とは異なり, data を隠ぺいすることができる。
a = {}
local x = 20
for i=1,10 do
local y = 0
a[i] = function () y=y+1; return x+y end
end
クロージャはすべて同じ x を参照するが、それぞれ異なった y を参照する。
(y の解決を実行時の環境ではなく、 定義時の環境で行っているため.
for i=1,10 do
print(a[i]())
end
-- 実行結果は 21 が 10 個表示される
■ Math(数値演算)
■ MathModule
math.max(x, ...)
math.min(x, ...)
math.floor(x)
math.ceil(x)
math.abs(x)
math.pi
math.random
math.sqrt
■ IO.FileSystem
POINT
CRT stdio を拡張したもの
fp = io.input("text" );
io.input(); -- console に戻す.
io.output("text" );
-- \n ふくまず
io.write();
-- console に戻す
io.output();
-- file handle 経由
do
local file = io.open("d:/lua/test.txt" , "r" );
if ( file ) then
-- filehandle:read( format );
-- [ *l | *a | *n ]
local t = file:read("*a" );
print(t);
else
print("no file" );
end
end
-- file handle 経由
local fp = io.open("d:/lua/test.txt" , "w" );
if ( fp ) then
-- fp:read( format );
-- [ *l | *a | *n ]
local t = fp:read("*a" );
print(t);
else
print("no file" );
end
WARNING
当然ながら文字コードが SHIFT_JIS や UTF-8 は問題ないが UNICODE はうまく動かない
POINT
file 入出力 library も oo を利用する
( filehandle は userdata )
fp = io.open( file, "r" );
line = fp:read();
f = io.open( "test.txt" , "r" )
out = io.open("out.txt" ,"w" )
-- 各行を処理をする
for line in f:lines() do
out:write(line.."\n" )
print(line)
end
out:close()
f:close()
■ OS
OS が提供する機能を提供する.
os.date()
os.time()
os.remove( filename );
os.rename( filename );
■ Process
外部プロセスの実行は os.execute() を使う
-- 処理はブロックされる
os.execute( "ping localhost" );
-- ブロックを回避するには start コマンドを使う。
os.execute( "start notepad" );
-- 終了コードをとる
local ret = os.execute( "ping localhost" );
■ Type(Data型)
■ 型の種類
Data 型とは [dataをどう扱うか(どう見るか)] の形式
計算機内部では すべての情報は 数値として扱われる.
文字列型 . bit列 を 文字として解釈する.
nil
number
string
boolean
table
function
thread
userdata ( C のメモリブロック )
-- 型をしらべる
local n = "aaa" ;
print ( type(n) );
number : double( luaconf.h で変更可能 )
usrdata: 任意の C の data を格納する. ( == 生の memblock ) -> ( 代入, 比較 )演算のみ定義
thread : 実行している thread を格納. ( coroutign の実装のために使用 )
Lua Thread と OS Thread は異なる.
table : 連想配列. [ == KEY が数値以外に任意の値がOK ]
table は異種混合可能. ( 関数をおくことも可能 )
この table が Lua での 唯一の data 構造.
record を table を利用して, 表現してみる.
field名 == index として利用する.
a.test == a["test" ];
table thread function usrdata の値は参照.
■ キャスト(cast)
Luaは文字列と数値を実行時に自動的に変換してくれる。
-- number -> string
c = 10 + 20;
String.print( c );
Number -> 文字列演算 -> String
string -> 数値演算 -> Number
nr = nr + "1" ;
nr += "35" ; -- これはダメらしい.
文字列と数値の結合ができる
-- "number = 10"
"number = " .. 10
WARNING
bool は .. で文字列に cast できない?
POINT
C では float -> int では自動的に少数部が切り捨てられる.
Lua では数値という概念しかないので, 切り捨てられることはない.
-- math.abs() の型 chk version を用意する.
function myabs(n)
if(type(n) ~= "number" ) then
print(n);
error("must be a number prm" );
end
print(math.abs(n));
end
myabs( "10" );
WARNING
だたし等号をとるときは, 同じ型である必要がある。
if "107" == "107" then
print ( "true" )
end
■ 代入演算子 [ = ] の左側にかける[ 式 ]を [ 左辺値 ] という
loadstring("print(_VERSION)" )()
loadstring("print(_VERSION)" ) -- 戻り値は関数.
POINT
戻り値として使用する関数を, 高階関数という.
table を返して, すぐに利用する.
function test()
arr = { 1, 2, 3 };
return arr;
end
do
test()[2] = 10;
print( test()[3] );
end
■ GC Luaは自動的にメモリを管理する
ガベージコレクタ を時々実行することで、自動的にメモリを管理する
Luaはインクリメンタルマークアンドスイープコレクタを実装している
ガベージコレクタ停止値 と ガベージコレクタステップ係数 を使うことができる
-> usr が調整可能.
■ ガベージコレクションメタメソッド
ユーザーデータにガベージコレクタメタメソッドを設定する. ( hook みたいなもの ? )
使う目的:
[ Lua GC を 外部のリソースに利用ささせる. ]
-> 独自に確保した work を解放する. -> finalizer と呼ばれる.
メタテーブルに __gc フィールドをもつ, usrdata に以下の関数相当のことをする.
function gc_event (udata)
local h = metatable(udata).__gc
if h then
h(udata)
end
end
■ Cから実行する。
C < --- lua_toXXX() --- Lua ret = func( x, y );
C --- lua_pushXXX() return Nr; ---> Lua
pushXXX : 結果を stack に のせる.
toXXX : スタック から値を取得 ( stack は取り除かれない )
Ret2 < --- ここから Lua は Nr 個を script の変数に順番に格納
-------
Ret1
-------
Arg2
-------
Arg1
-------
WARNING
// C++ からリンクする時は "C" リンケージをすること。
// Link エラーになる。
#include "lua.hpp"
#pragma comment(lib, "../../lib/lua51.lib" )
#pragma comment(lib, "../../lib/lua5.1.lib" )
int main()
{
lua_State *s;
s = lua_open();
luaL_openlibs( s );
luaL_dofile( s, "d:/test.lua" );
lua_close( s );
return 0;
}
ビルドする。
Lua スクリプトを用意して実行してもらう。
■ Cの関数をバインドする
POINT
Lua < -> C で Data を交換するのは, すべてスタック経由
Lua からの引数もスタック経由でうけとり、 C からの返値をスタックにのせる
int add( lua_State *s )
{
printf( "call func\n" );
// 第2引数をスタックから int として取得
int n2 = luaL_checkint(s, -2);
// 第一引数
int n1 = luaL_checkint(s, -1);
int ret = n1 + n2;
// 戻値をスタックに積む
lua_pushnumber( s, ret );
// スタックに積んだ数を返す。
return 1;
}
int main()
{
printf( "test\n" );
lua_State *s;
s = lua_open();
luaL_openlibs( s );
// add
lua_register(s, "add" , add );
//luaL_dofile( s, path.c_str() );
luaL_dofile( s, "d:/work/pro/lua/script/test.lua" );
lua_close( s );
return 0;
}
■ C++のクラスをバインドする
// テストクラス
class Test
{
int _x;
public:
Test(int x) : _x(x) {
printf("ctor: %p, %d\n" , this, _x);
}
~Test() {
printf("dtor: %p\n" , this);
}
void talk() {
printf("talk(), %p, %d\n" , this, _x);
}
void *operator new ( size_t sz, void *mem ) {
return mem;
}
};
メソッドをコールするために lua へバインドするための関数でラップする。
static int l_talk(lua_State* L)
{
// ユーザデータ(ポインタ)を Lua Script からもらう。
Test* p = (Test*)lua_touserdata(L, -1);
// メソッド本体をコール
p->talk();
// 戻り値の数
return 0;
}
Lua から new をしたら, C++ 側では実際にオブジェクトを作成する。
さらにメソッドをフィールドアクセスの構文( p.method() )で呼べるように
__index を指定したメタテーブルをオブジェクトに設定する。
int l_new( lua_State *s )
{
// Lua 側から数値パラメータを一個もらう。
int i = lua_tonumber( s, -1 );
// オブジェクトを new して、そのポインタを Luaスタックに積む。
void *p = lua_newuserdata ( s, sizeof( Test ) );
void *q = new(p) Test( i );
// クロージャで関連づけたテーブルを取り出し、
lua_pushvalue( s, lua_upvalueindex(1));
// メタテーブルとして設定する
lua_setmetatable( s, -2);
// ポインタを Lua Script 側へ返す。
// これで Lua 側からオブジェクトをハンドルできる。
return 1;
}
Lua からは次のように作成する。
-- オブジェクトを生成して、ハンドルをもらう。
-- Test テーブルの new メソッドをコールする。
local p = Test.new( 5 );
-- 操作したいハンドルを指定してメソッドをコールする。
-- オブジェクト p に対して p.talk と書けるのはフィールドアクセス( __index )をオーバーライドしたから
p.talk( p );
-- : を利用することで、第一引数を省略できメソッド的な記述ができる。
-- やっているのは上と同じ。
p:talk();
-- 別オブジェクトをつくる。
local p2 = Test.new( 3 );
void defclass( lua_State *s )
{
// クラスをテーブルとして生成
lua_newtable( s );
// ユーザデータで共有するメタテーブル用テーブル作成
// インスタンス毎に setmetatable される
lua_newtable( s );
// __index をオーバーライドすることで obj.method() という記述した( フィールドアクセス )時に
// メソッド用関数のテーブルを参照させる。
// setfield によって値がポップするためコピーしておく。
lua_pushvalue(s, -1); // メタテーブル自身
lua_setfield( s, -2, "__index" );
// クラスのメソッドをテーブルを登録しておく
lua_pushcfunction( s, l_talk );
lua_setfield( s, -2, "talk" );
// テーブルを上位値としてクロージャ生成
// 後で new() をした時にテーブルを取り出し instance のメタテーブルとして設定する。
lua_pushcclosure( s, l_new, 1);
lua_setfield( s, -2, "new" );
// 生成したクラス(テーブル)を Test という名前でグローバル変数に公開
lua_setglobal( s, "Test" );
}
■ Reference
■ util
■ lua_type
SYNTAX
int lua_type (lua_State *s, int index );
DESC
index で指定した値の型を返す。
LUA_TNIL
LUA_TNUMBER
LUA_TBOOLEAN
LUA_TSTRING
LUA_TTABLE
LUA_TFUNCTION
LUA_TUSERDATA
LUA_TTHREAD
LUA_TLIGHTUSERDATA
■ lua_typename
SYNTAX
const char *lua_typename (lua_State *L, int tp);
DESC
lua_type で取得した型から型名を返す。
int t = lua_type( s, idx );
printf( "%d %s" , idx, lua_type(s, t) );
■ lua_gettop
SYNTAX
int lua_gettop (lua_State *)
DESC
スタックトップのインデックスを返す。
インデックスは1から始まるのでスタックサイズを取得できる。
■ スタック操作
POINT
1つの関数内で使用できる, stack の大きさは 256 程度
limits.h MAXSTACK で変更できる。
■ lua_pop
SYNTAX
void lua_pop (lua_State *L, int n);
DESC
stack 上位から [ N個 ]の要素を取り除く.
POINT
stack 数以上を削除すると, 空になる
■ lua_remove
SYNTAX
void lua_remove (lua_State *s, int index);
DESC
指定した有効なインデックスの要素を取り除き、 上の要素をずらして隙間を埋める
疑似インデックスは、実際のスタック位置でないため指定できない
■ lua_settop
SYNTAX
lua_settop( lua_State *, int index );
DESC
index 番目の要素を top にして それより上を削除 ( メタファは上を刈り取る )
余計な値を除去する時に使う。
現在値より上段を指定すると nil で埋められる。
// index 0 を指定するとスタックは空( というかインデックス位置が移動するため無効 )になる
< - 現在地( 3 )
------- -------
A
------- -------
B
------- ------- < - ( 0 番目 )
lua_settop( s, 2 );
-------- --------
key -->
-------- --------
table
-------- --------
bbb bbb < -- 2 番目をトップに指定
-------- --------
ccc ccc
-------- --------
■ スタックからの取得
POINT
Lua から C の関数を呼ぶときに
Lua で引数を指定して
C 側からスタックに積まれた引数を取得。
スタックの要素の位置は +- で指定できる。
-2 というのは stack 上位から2 番目の要素
+ : 下から N 番目
- : 上から N 番目
[ ] < - -1
[ ]
[ ]
[ ] < - 1
仮想スタックは Lua と Host が data を交換する Object
Lua が C の関数をコールする際は, 以前のスタックを破壊しないように, 新規stackを作成する.
スタックは関数ごとに用意される. ( というより, Arg 以下が隠される. )
lua_gettop() で確認できる。
stack は関数ごとに用意される.
[ 関数の中では ] [ stack が 1 から利用 ]される. -> ( 厳密には 不要な部分が隠蔽される )
3 param, 2 ret
// prm
---
3
---
2
---
1
---
// ret ( 終わった時点で stack にのっている点に注意. )
---
2
---
1
---
■ lua_tonumber
SYNTAX
lua_Number lua_tonumber (lua_State *L, int index);
DESC
index の位置にある値を lua_Number 型に変換して返す。
数値 または 数値に変換できる文字列である必要がある。
値を参照するだけで、スタックの数は変わらない。
RET
0 : 変換できなかった。
STACK
[-0, +0, -]
-- 1番目の引数から順番にスタックにつまれる。
add( 100, 200 );
----------
200
----------
100
----------
// C 側は引数をスタックから取得して何かの処理をして、必要なら結果をスタックにのせる。
int l_add( lua_State *s ) {
int arg0 = lua_tonumber( s, -2 ); // 上から 2 番目をとる
int arg1 = lua_tonumber( s, -1 ); // 上から 1 番目をとる
int ret = arg0 + arg1;
lua_pushnumber( ret ); // スタックに結果をつむ
return 1; // 結果の数を Lua に教える
}
■ lua_touserdata
SYNTAX
void *lua_touserdata (lua_State *L, int index);
DESC
指定 index 値がフルユーザーデータ( または lightuserdata )であれば
そのブロックのアドレスを返す
lightuserdata であれば、そのポインタを返す
どちらでもなければ null
-- Window のポインタを受け取り、
w = CreateWindow();
-- ポインタ経由で操作してもらう。
SetWindowText( w, "test" );
SetWindowSize( w, 600, 400 );
local w, h = GetWindowSize( w );
l_createWindow( lua_State *) {
lua_newuserdata( s );
Window p = new Window();
return 1;
}
l_setWindowText( lua_State *) {
lua_touserdata( s, -2 );
lua_tostring ( s, -1 );
p->setText( s )
}
■ lua_tostring
SYNTAX
const char *lua_tostring (lua_State *L, int index);
DESC
lua_tolstring を呼ぶのと同じ
WARNING
Lua -> C への 文字列の受け渡し( adress )は Lua の GC によって回収される可能性がある
POINT
逆に C -> Lua ( lua_pushstring() ) は 文字列をコピーするので
C 側での文字列のメモリは開放、変更をしてもよい。
■ lua_tolstring
SYNTAX
const char *lua_tolstring (lua_State *L, int index, size_t *len);
DESC
指定 index がさす値を 文字列に変換する.( stack 内が変化する )
index は [ 文字列 | 数値 ] を指す必要あり
len != NULL: 文字数をセット.
WARNING
stack 内部の alignment された pointer をかえす
( GC される可能性あり )
■ lua_getglobal
SYNTAX
void lua_getglobal( lua_State *s, const char *name );
DESC
lua_getfield の限定版
グローバル変数 name の値をスタックに積む。
STACK
[-0, +1, e]
POINT
マクロとして定義されている。
#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, s)
POINT
ブロック外で定義した Var, Fn はすべて TblGlobal に存在する.
-->
-------- --------
fn | var
-------- --------
string string
-------- --------
-- lua
config = {
width = 600,
height = 400,
};
// cpp
{
lua_getglobal( s, "config" );
lua_getfield( s, -1, "width" );
int v = luaL_checknumber( s, -1 );
}
■ 生成
■ lua_createtable
SYNTAX
void lua_createtable (lua_State *L, int narr, int nrec);
STACK
[-0, +1, m]
DESC
空のテーブルを作成してスタックに追加。
The new table has space pre-allocated for narr array elements
and nrec non-array elements.
This pre-allocation is useful
when you know exactly how many elements the table will have.
■ lua_newuserdata
SYNTAX
void *lua_newuserdata (lua_State *, size_t size);
STACK
[-0, +1, m]
DESC
指定 size の メモリをわりあて, そのアドレスをもつ fulluserdata をスタックにつむ。
fulluserdata は メモリブロックをあらわし, 独自の[ metatable ] を持つことができる。
table も metatable を持つことができる。
WARNING
Lua が解放するので free() 禁止.
GC 回収の際に memory address を変更する場合もあるかも
POINT
TblGbl , Tbl に値をセットしておくことで, [ 使用中 ]の扱いになるので, 解放はされない.
local 変数だと問題がある。
data が必要なくなった場合は, [ __gc ] に指定する MetaMethod を利用する.
-------- --------
userdata
-------- --------
number --> number
-------- --------
int create_rgb( lua_state *s )
{
char *data;
// 4 Byte 確保. ( Lua 版 malloc と同じ )
data = (char *)lua_newuserdata( s, 4 );
memsset( data, 0, 4 );
return 1;
}
■ lua_newtable
SYNTAX
void lua_newtable (lua_State *s);
DESC
新しい空のテーブルを作ってスタックに積む
lua_createtable(L, 0, 0) と同じ。
-------- --------
table
-------- --------
number --> number
-------- --------
■ lua_pushinteger
SYNTAX
void lua_pushinteger (lua_State *s, lua_Integer n);
DESC
値 n を持つ数値をスタックに積む
■ lua_pushnumber
SYNTAX
void lua_pushnumber (lua_state *s, lua_Number n);
desc
n を stack top へ積む。
WARNING
うけとる文字列は, Lua 側でGCされる可能性あり
-------- --------
number
-------- --> --------
string string
-------- --------
■ lua_pushnil
SYNTAX
void lua_pushnil (lua_State *s);
desc
nil 値を stack に積む.
■ lua_pushvalue
SYNTAX
void lua_pushvalue (lua_State *L, int index);
DESC
指定した有効なインデックスにある要素の[ コピー ]をスタックに積む
有効な index top でなくても OK
STACK
[-0, +1, -]
lua_pushvalue( s, -1 );
-------- --------
table
-------- --> --------
table table
-------- --------
■ lua_setfield
SYNTAX
void lua_setfield (lua_State *L, int index, const char *key);
STACK
[-1, +0, e]
DESC
index で指定したテーブルに key の名前でスタック最上段の値を登録する。
値はポップされる。
table[ key ] = value という設定をする。
Lua の中では, newindex meta method が呼ばれる可能性がある.
Tbl に G を指定すれば, VarGbl にも指定可能.
As in Lua,
this function may trigger a metamethod for the "newindex" event
lua_pushnumber( s, 10 );
lua_setfield( s, -2, "test" );
-------- --------
10 -->
-------- --------
table table( test=10 )
-------- --------
■ lua_settable
SYNTAX
void lua_settable (lua_State *L, int index);
DESC
スタック最上位に値、キーをつみ、
index で指定したテーブルの位置にフィールドを追加する。
POINT
lua_setfield() は特殊系
-------- --------
val -->
-------- --------
key
-------- --------
table table
-------- --------
POINT
newindex event の MetaMethod を呼ぶ.
■ lua_rawset
SYNTAX
void lua_rawset (lua_State *L, int index);
DESC
lua_settable と同じだが 生の代入である点が異なる (メタメソッドを呼ばない)。
index は table を指す index
stack から { KEY, VAL } == {下, 上}は pop される. --> field と同じと考えよう ^ ^/
VAL
---
KEY
これと同じ
t = {a=10, b=20};
lua_rawset( s, LUA_ENVIRONINDEX );
lua_rawset( s, -3 ); // stack top から 3番目の table へアクセス
■ lua_rawget
SYNTAX
void lua_rawget (lua_State *L, int index);
DESC
lua_gettable と同じ。
スタック最上段につんだキーの値で index が指すテーブルから値をとる。
metamethod は起動しない。
t = {a=10, b=20}; Lua でやっていることを, LuaAPI でしているだけ
lua_rawset( s, LUA_ENVIRONINDEX );
lua_rawset( s, -3 ); // stack top から 3番目の table へアクセス
POINT
指定した KEY がない場合は,
nil をスタックトップへ返す。
■ lua_rawgeti
SYNTAX
void lua_rawgeti (lua_State *s, int idxTbl, int n );
DESC
lua_rawget のキーの値を整数の n に限定した関数。
table[ n ] の結果をスタックへ積む。
( MetaMethod はよばれない )
STACK
[-0, +1, -]
POP
tbl[n];
lua_rawgeti( s, -2, 1 );
-------- --------
value
-------- --------
string --> string
-------- --------
table table
-------- --------
■ lua_getfield
SYNTAX
void lua_getfield (lua_State *L, int index, const char *k);
desc
index が指す table["k" ] の値を stack top へ積む。
key は stack top の値
field 名を指定できるのが point
lua_rawset( s, LUA_ENVIRONINDEX );
POP
KEY の値に対応する VAL
POINT
指定した KEY がない場合は nil を返す。
-->
-------- --------
number
-------- --------
tbl tbl
-------- --------
// グローバルテーブルにアクセス
lua_getfield( s, LUA_GLOBALSINDEX, nameKey );
■ lua_gettable
SYNTAX
void lua_gettable (lua_State *L, int index);
DESC
キーとなる値をスタックトップへつむ
index が指す テーブル["key" ] の値をスタックが積まれ、
キーの値はスタックから捨てられる。
( また MetaMethod の index イベントがトリガーされる )
STACK
[-1, +1, e]
Ex.
lua_rawset( s, LUA_ENVIRONINDEX );
POINT
lua_getfield() は lua_gettable() のキーの型を文字列に限定した関数
指定した KEY がない場合は nil を返す.
-->
-------- --------
key value
-------- --------
table table
-------- --------
void lua_arraygeti( lua_state *s, int tblindex, int i )
{
// stack 最上位に 配列の index をセット
lua_pushnumber( s, i );
lua_gettable( s, tblindex );
}
■ lua_insert
SYNTAX
void lua_insert (lua_State *L, int index);
STACK
[-1, +1, -]
DESC
スタック最上段の値を index の位置へ移動する。
index の位置より上の値は上へシフトする。
-->
-------- --------
string number
-------- --------
number string
-------- --------
table table
-------- --------
■ lua_setglobal
SYNTAX
void lua_setglobal (lua_State *L, const char *name);
DESC
スタック最上段の値を指定した名前でグロバール変数として登録する。
登録する値はスタックからポップされる。
関数がpushされていれば関数に名前をつけて公開することになる。
STACK
[-1, +0, e]
#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, s)
lua_setglobal( "testfunc" )
-------- --------
function -->
-------- --------
table table
-------- --------
■ lua_pushcfunction
SYNTAX
void lua_pushcfunction (lua_State *L, lua_CFunction f);
STACK
[-0, +1, m]
DESC
関数へのポインタを受け取り、関数型のLua値をスタックに積む
関数のシグネーチャは一致させること。
POINT
lua_pushcclosure(L, f, 0) と同等
これによって、Lua Script 内の任意の場所から関数呼び出しができる。
POINT
#define lua_pushcfunction(L,f) lua_pushcclosure(L,f,0)
#define lua_register(L,n,f) (lua_pushcfunction(L, f), lua_setglobal(L, n))
■ lua_register
DESC
C の関数を lua の関数として登録する
lua_register( s, l_func, "func" );
■ lua_pushcclosure
SYNTAX
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n );
STACK
[-n, +1, m]
DESC
Cのクロージャをスタックへつむ。
このとき関連した値を設定できる。
関連づける値は先にスタックへ積んでおく。
クロージャが作成されるとこの値はスタックからポップされる。
lua_pushcclosure ( s, fn, 2 );
-------- --------
arg1 -->
-------- --------
arg0 --> function(arg0, arg1)
-------- --------
table table
-------- --------
aaa aaa
-------- --------
■ lua_pushlightuserdata
SYNTAX
void lua_pushlightuserdata (lua_state *s, void *p);
DESC
lightuserdata(ポインタ)をスタックに積む
POINT
lightuserdata とは host object のポインタのこと
作成はせず、メタテーブルも持たず、(作成しないので) 回収もされない
// C 側で Window をつくり
int CreateWindow( lua_State *s )
{
void *p = new Window( 1 );
lua_pushlightuserdata( s, p );
return 1;
}
// 操作させる
int SetWindowSize( lua_State *s )
{
Window* p = (Window *)lua_touserdata(s, -1);
int i = lua_tonumber(s, -2);
p->setSize( i );
}
-- lua
local p = CreateWindow();
SetWindowSize( p, 100 );
■ C Closures
C の関数を作るときに、特定の値を関連づけることができる。
これらは upvalue と呼ばれる。
upvalue は関数をコールされたときに参照できる。
参照へのインデックス( 最大 256 )は lua_upvalueindex を利用する。
■ メタテーブル
通常のテーブルと同じだが、
メタテーブルのキーに対して, 関数などを設定することで + などの演算を上書きして
独自の動作をさせることができる
C++ での operator +() と同じ。
Lua の各値はメタテーブルをもつ。
クラスのインスタンスを作ったときに メタテーブルを設定しておくと
+ などで独自の設定ができる。
メタテーブルのキーは getmetatable() でリストできる。
Every value in Lua can have a metatable.
This metatable is an ordinary Lua table that
defines the behavior of the original value
under certain special operations.
You can change several aspects of the behavior of operations over a value
by setting specific fields in its metatable.
For instance,
when a non-numeric value is the operand of an addition,
Lua checks for a function in the field "__add" in its metatable.
If it finds one,
Lua calls this function to perform the addition.
We call the keys in a metatable events and the values metamethods.
In the previous example,
the event is "add" and the metamethod is the function that performs the addition.
You can query the metatable of any value
through the getmetatable function.
You can replace the metatable of tables
through the setmetatable function.
You cannot change the metatable of other types from Lua (except by using the debug library);
you must use the C API for that.
Tables and full userdata have individual metatables
(although multiple tables and userdata can share their metatables).
Values of all other types share one single metatable per type; that is,
there is one single metatable for all numbers, one for all strings, etc.
A metatable controls how an object behaves in arithmetic operations,
order comparisons,
concatenation,
length operation,
and indexing.
A metatable also can define a function to be called
when a userdata is garbage collected.
For each of these operations Lua associates a specific key called an event.
When Lua performs one of these operations over a value,
it checks whether this value has a metatable with the corresponding event.
If so, the value associated with that key (the metamethod) controls
how Lua will perform the operation.
Metatables control the operations listed next.
Each operation is identified by its corresponding name.
The code shown here in Lua is only illustrative;
the real behavior is hard coded in the interpreter
and it is much more efficient than this simulation.
All functions used in these descriptions (rawget, tonumber, etc.) are described in §5.1.
In particular,
to retrieve the metamethod of a given object,
we use the expression
metatable(obj)[event]
■ lua_setmetatable
SYNTAX
int lua_setmetatable (lua_State *L, int index);
DESC
index で指定したオブジェクトにスタック最上段のテーブルをメタテーブルとして設定する。
最上段のテーブルはPOPされる。
lua_setmetatable( s, -2 );
-------- --------
table -->
-------- --------
userdata userdata(table)
-------- --------
aaa aaa
-------- --------
■ luaL_newmetatable
SYNTAX
int luaL_newmetatable (lua_State *L, const char *tname);
DESC
registory( table ) に KEY [ tname ] があれば, 0 を返す.
そうでなければ, userdata の metatable として使う 新しい table を作る.
registory tname に関連づいた値( VAL )を stack に積む.
■ lua_getmetatable
SYNTAX
int lua_getmetatable (lua_State *L, int index);
DESC
index がさすオブジェクトが持つ metatable を stacktop へつむ.
RET
0 : 失敗。index が無効。または 指定したオブジェクトが metatable をもっていない。
■ 型チェック
WARNING
[ 型を調べる ]ことで適切な処理をする
スタックの操作を間違えると処理がとまる。
■ lua_isstring
■ lua_isfunction
■ lua_istable
SYNTAX
int lua_istable (lua_State *L, int index);
DESC
指定した index のさす値がテーブルであれば1、そうでなければ0を返す
■ lua_isnil
SYNTAX
int lua_isnil (lua_State *L, int index);
DESC
指定 index の値が nil ならば 1 else 0
■ luaL_checkstring
SYNTAX
const char *luaL_checkstring (lua_State *L, int narg);
DESC
関数の第 narg 引数が文字列であるかをチェックし
その文字列を返す
チェックに不合格ながらエラーをなげる。
POINT
Lua -> C
■ luaL_checknumber
SYNTAX
lua_Number luaL_checknumber (lua_State *L, int narg);
DESC
関数の第 narg 引数が数値であるかをチェックし
その数値を返す
stack から値を取得するので, 関数呼び出しでなくても利用可能.
この場合は stack 全体を index 指定することになる.
関数の引数は指定した順番に stack にのる。
POINT
スクリプトの引数のチェックができる
-> number expected, got string
■ luaL_typerror
SYNTAX
int luaL_typerror (lua_State *L, int narg, const char *tname);
DESC
次の message を出力する
型に対してアサートする。
< location>: bad argument < narg> to < function> (< tname> expected, got < realt>)
■ luaL_checkudata
SYNTAX
void *luaL_checkudata (lua_State *L, int narg, const char *tname);
DESC
関数の第 narg 引数が [ 型 tname ] のユーザーデータであるかチェックする
■ その他
■ lua_error
SYNTAX
int lua_error (lua_State *L);
STACK
[-1, +0, v]
DESC
Lua のエラーを発生させる
スタックトップの値がエラーメッセージに使われる。
long jump を利用しないため、戻ることはない。
■ luaL_dofile
SYNTAX
void luaL_dofile( s, const char *path );
DESC
path から lua file を読み込む.
function は [ load ] する && 実行文は そのまま[ 実行 ]
-> LUA_GLOBALSINDEX がさす table に格納される
= { "nameFunction" , funcbody };
関数 はたぶん reload される ?
-> table の要素数を取得すれば OK
#define luaL_dofile(L, fn) (luaL_loadfile(L, fn) || lua_pcall(L, 0, 0, 0))
■ luaL_dostring
SYNTAX
luaL_dostring( s, "code..." );
DESC
C言語から Lua interpreter 呼んで, 処理をする。
RET
0 : 成功
!0 : 失敗
POINT
Lua での制御タイミングを提供する。
■ luaL_loadfile
SYNTAX
int luaL_loadfile(lua_State *L, const char *filename);
DESC
ファイルから LuaChunk をロードして compile した Chunk を Stack に積む
■ lua_call
SYNTAX
void lua_call(lua_State *s, int nrArg, int nrRet );
DESC
Lua 関数を C からコールする。
コールする関数と引数の順番にスタックへ積む。
関数の戻値は順番通りに (最初の戻値が最初に) スタックに積まれる
PUSH
返値( nrArg で指定 , LUA_MULTRET の場合は, すべての返値がかえる )
// 引数と関数がポップされて、結果がスタックにのる。
lua_call( s, 2, 1 );
-------- --------
arg -->
-------- --------
arg
-------- --------
function ret
-------- --------
POINT
nrArg : Lua は nrArg + 1 に関数があることを知る. ( 間違えると落ちる. )
func( arg1, arg2 ) には順番に Stk からセットされる.
nrRet : Lua に 何個の返値を Stk に積むか教える. ( -> 嘘つくと nil がつまれる )
// 以下の Lua Code と等価なことを C でやってみる.
// Lua の関数は LUA_GLOBALSINDEX に登録されている
lua_getfield(s, LUA_GLOBALSINDEX, "f" );
lua_getglobal( s, "f" ); // これは同じ
// 第一引数をセット
lua_pushstring(s, "test" );
// table も LUA_GLOBALSINDEX にある
lua_getfield(s, LUA_GLOBALSINDEX, "t" );
lua_getfield(s, -1, "x" ); /* t.xの結果(2番目の引数)を積む */
lua_remove(s, -2); /* スタックから `t' を取り除く */
// 第3引数
lua_pushinteger(s, 14);
// 3個の引数と1個の戻値で関数をコール
lua_call(s, 3, 1);
// Lua は global 変数は table[ KEY = VAL ] という特性をうまく利用している.
// グローバル変数 a に代入
lua_setfield(s, LUA_GLOBALSINDEX, "ret" );
■ lua_pcall
SYNTAX
int lua_pcall(lua_State *s, int nrArg, int nrRet, int errfunc);
DESC
Protected Mode で関数をコールする。
エラーがなければ, lua_call と同じ処理。
エラーが発生すると、エラーメッセージをスタックへ積みエラーコードを返す。
errfunc で指定したエラーハンドラー関数があればエラーメッセージを引数に呼ばれる。
スタックトレースなどの追加情報をいれるために使う。
RET
0 : 成功
N : 失敗( lua.h )
POINT
失敗した場合は, lua_pop() する
■ lua_replace
SYNTAX
void lua_replace (lua_State *L, int index);
DESC
スタックトップの要素を指定した位置に移動する. ( 他の要素は変わらない点に注意. )
------ ------
string
------ ------
20 20
------ ------
30 string
------ ------
lua_repalce( s, 1 );
■ lua_getfenv
SYNTAX
void lua_getfenv (lua_State *s, int index);
desc
指定した index の値の環境テーブルをスタックに積む
PUSH
指定 index がさす table
■ lua_setfenv
SYNTAX
int lua_setfenv (lua_State *s, int index);
DESC
index が指す値が 関数, thread, userdata ならば, stacktop の
環境テーブルを新しい環境として set する.
POP
stacktop の EnvTable
■ luaL_openlibs
SYNTAX
void luaL_openlibs (lua_state *s);
DESC
指定したステートにすべての標準Luaライブラリを開く
String.print() などに利用すること
C SRT で実装されている.
■ luaL_getmetatable
SYNTAX
void luaL_getmetatable (lua_State *L, const char *tname);
DESC
registory( == table )の名前 tname に関連付けられたメタテーブルをスタックに積む
PUSH
tname に関連つけられた metatable
■ luaL_register
SYNTAX
void luaL_register( lua_State *L, const char *libname, const luaL_Reg *l);
DESC
stack top にその table をおく
libname がNULLでなければ新しいテーブル t を作成し、
それをグローバル変数 libname の値として設定し
POINT
Lua関数名 が KEY, Host関数 が VAL の Table
■ lua_rawequal
SYNTAX
int lua_rawequal (lua_State *L, int index1, int index2);
DESC
index1 と index2 がプリミティブに等しい
(つまりメタメソッドを呼ばない) とき1を返す。
そうでなければ0を返す。
どちらかのインデックスが有効でないときも0を返す。
■ metatable(MetaMethod)
POINT
MetaMethod を利用して, Lua を拡張(カスタマイズ)できる。
■ 自分で作成した型を調べる
type( data ) == "table"
if ( getmetatable( g ) == rgbmeta ) then
print("custom data" );
end
■ 配列の上限値を設定する
( MetaMethod を利用することで テーブル要素の参照をカスタマイズする
__index, __newindex を上書きする.
__index : val = tbl["name" ]; // 参照.
__newindex : tbl["name" ] = "foo" ; // 代入
MetaMethod の定義中に, MetaMethod を呼ぶのを防ぐのは, rawget, rawset()
-- rawget( tbl, idx );
val = tbl[idx]
-- rawset( tbl, idx, val );
tbl[idx] = val
--
-- MetaTbl
-- この tbl EventKey に対して OverWrite 関数をセットする.
tblTest = { limit = 3 };
--
-- tbl を参照する [] 演算子を overwrite する
--
tblTest.__index = function( t, idx )
if ( idx > tblTest.limit ) then
error("invalid dana idx" );
end
return rawget( t, idx );
end
-- メタテーブルをセットする。
tt = {}
setmetatable( tt, tblTest );
-- テスト
print( tt[10] );
■ luaRef
■ rawget
SYNTAX
rawget(table, key)
table … テーブル型
key … 任意の型
DESC
メタメソッドを用いずに
テーブルtableのkeyをキーとする要素の値(table[key])を返す。
■ dofile
SYNTAX
dofile( string file )
DESC
filenameで指定されたファイルを読み、Luaスクリプトとして実行する。
file の指定がない場合は stdin から読む。
RET
スクリプト全体の返値
■ error
SYNTAX
error (message [, level])
DESC
最後に保護された関数呼び出しを終了し、
message をエラーメッセージとして返す
■ setmetatable
SYNTAX
setmetatable (table, metatable)
DESC
table の metatable を変更する.
RET
table
POP
Ex.
setmetatable( t, nil ); table[ t ]から metatable を除去する.
POINT
WARNING
■ print
SYNTAX
print( arg ... )
DESC
tostring で[ 文字列に変換 ]してから標準出力 (stdout) に表示
BAD
print 10;
■ GarbageCollection
文字列など,登録されないものを, 回収する.
Lua が Table を扱う仕組みについて, かんがえる
table data は 内容が同一でも, 異なるworkに確保される.
[ 変数はdataの場所を参照しているだけ ]
■ collectgarbage
SYNTAX
collectgarbage( string opt [, number arg ] )
DESC
GC を実行する。
■ その他
■ DEBUG
local v = 0;
function testHook()
print("Debug v = " ..v);
end
-- 実行文がよばれる直前に testHook を呼ぶ.
debug.sethook( testHook, "l" );
v = v + 1;
POINT
最終的にスタックは元の状態に戻っている
これは良いプログラミング作法
■ Coroutine(Co-routine)
複数の coroutine を作成できるが, 平行して動作することはできない.
互いに譲りあい( yield ) ながら作業をする。
協調的 multithread
OS が CPU の実行を切り替えながらする作業を,
PreemptiveMultiTask という.
先制. ( 先買権 == はやいもの勝ち )
[ 実行処理自体 == worker ]が 切り替えの合図を送るのが,
NonPreenptiveMultiTask
だから協調 ( だから yield )
POINT
Coroutine とは 実行を一時中断して, 後で再開する仕組みのこと。
自分で中断を宣言することが、 OS の管理する multithread とは異なる。
自分で yield() をコールして初めて中断される。
Coroutign 作成. ( worker ) // thread object をかえす.
thread = coroutine.create;
2. main() 関数にわたす. ( Job )
main( thread, prm ... );
3. coroutine を稼働させる.
coroutine.resume() で稼働させる.
4. 終了をまつか, yield を実行する.
条件式は任意の値OK.( false と nil は共に偽 )
■ Luaのつかいみち
設定ファイルとして扱う
Lua format を利用する
Var=Val;( これを自分で解釈( Interprete )する )
-- lua でグローバル変数を設定。
var=10;
foo.cpp
lua_getglobal( s, "var" ); // globalTbl から var key stack にのせる
luaL_checknumber( s, -1 ); // stack から値を取得する.
C から lua のグローバル変数を設定する。
lua_pushstring(s, "test" );
lua_setfield(s, LUA_GLOBALSINDEX, "name" );
■ C 言語が主体
指定したところで, Lua にうつり, 処理がおわったら Lua から戻る。
ある処理における Callback の際に使う。
MVC でいうところの Control を Lua から制御する
Controler の機能を Lua に公開する
Controller, Data ともに C 側にある
■ Lua が主体
Lua 側に data, Controler を配置. ( Lua の data 構造を利用可能. )
設定 file は Lua 変数を扱うだけになる.
Gui は Lua でサポートされる必要がある.
Controller, Data ともに Lua 側にある.
■ hash
Hash とは,
文字列を数値に変換したもの. ( 検索する際に数値に変換して高速に検索する )
均等に分布するのが良い Hash 関数。
比較を用意にすることが目的.
Lua は内部に Hash 関数をもつ
Lua には Switch 文がない. ( if-elseif ... | table )で代用.
■ _G
DESC
グローバル環境を保持するグローバル変数
[ Lua の環境 ]と呼ばれる, global 変数をもつテーブルのこと。
POINT
__index : field に値を入れる時に使用される method.
a = 1; < -> _G.a = 1;
■ registory
registory とは 定義済みの [ table ]
好きなLuaの値を格納するために [ Cのコードから ]使うことができる.
疑似 index [ LUA_REGISTRYINDEX ]に置かれている
KEY が重複しないように注意すること.
Cオブジェクトのアドレスを持つライトユーザーデータ を用いるとよい.
■ lua_checkstack
SYNTAX
int lua_checkstack (lua_State *L, int n );
DESC
スタックに n の空きがあるかチェックする。
チェックするだけでスタックのサイズは変更しない。
STACK
[-0, +0, m]
RET
false : スタックに n 個の空きがない。
■ table に weakReference の設定をする
POINT
( table には 予約済みの field がある )
[ __mode ] field に "k" を含む -> KEY が weakReference
[ __mode ] field に "v" を含む -> VAL が weakReference
■ 擬似インデックス
スタック内に無いいくつかのLuaの値に[ Cのコードから ]アクセスするためのもの
stack 以外にある Lua が管理する値を 指すINDEX
どの関数も 疑似インデックス と呼ばれる有効なインデックスを受け付ける
LUA_GLOBALSINDEX : スレッドの環境 (グローバル変数がある場所)
LUA_ENVIRONINDEX : 実行中の C の関数の環境がある 擬似インデックス