入門Luaプログラミング第9章 Luaを拡張してみる(後編)

前回の続き、配列の上限値を設定したりグローバル変数を宣言できなくしたりオブジェクト指向を取り入れてみたり。

配列の上限値を設定する

前回のようにsetmetatable関数でまたしてもメタメソッドを上書きして、添字でアクセスされた場合の動作を書き換える。

> array = { max = 5 }
> array.__index = function(table, index)
>>   if array.max > index then
>>     return rawget(table, index)
>>   else
>>     return nil
>>   end
>> end
> array.__newindex = function(table, index, value)
>>   if array.max > index then
>>     rawset(table, index, value)
>>   else
>>     print("index > array.max")
>>   end
>> end
> 
> list = {}
> setmetatable(list, array)
> 
> for i = 1,7 do
>>   list[i] = i
>>   print(list[i])
>> end
1
2
3
4
index > array.max
nil
index > array.max
nil
index > array.max
nil

__indexが"a = b[1]"のような参照されたときに呼ばれるメソッドで、__newindexが"b[1] = 1"のような代入される場合に呼ばれるメソッド。
これらを上書きして5以上の添字の場合、nilを返したり文字を出力したりするようにしている。
だんだんわけのわからないことになってきてるような……

グローバル変数を宣言できないように

上のやつの応用…かな。

> safe = "ok"
> global = {}
> global.__index = function(table, index)
>>   print("null : __index")
>> end
> global.__newindex = function(table, index, value)
>>   print("null : __newindex")
>> end
> setmetatable(_G, global)
null : __index
> a = {}
null : __newindex
null : __index
> b = 1
null : __newindex
null : __index
> c = ''
null : __newindex
null : __index
> print(safe)
ok
null : __index

_Gはグローバル変数を保持しているテーブル。_Gの__indexと__newindexを何もしないようにする関数で置き換えてやると、setmetatable関数が呼ばれた後はグローバル変数を宣言しようとしてもできず、それより前に宣言されているものしか使えなくなるという……
まあ、使い方によってはいいかも。

オブジェクト指向を取り入れる

テーブルに関数をくっつけてオブジェクト指向風に?

> cursor = { x = 0, y = 0 }
> function cursor:move(x, y)
>>   cursor.x = x
>>   cursor.y = y
>> end
> cursor:move(2, 8)
> print(cursor.x)
2
> print(cursor.y)
8

"."でなく":"を使うところがなんとも。どうせなら"::"とかよかったなーなんて。C++風に。

function cursor:move(x, y)
-- (略)
end

function cursor.move(self, x, y)
-- (略)
end

cursor:move = function(x, y)
-- (略)
end

cursor.move = function(self, x, y)
-- (略)
end

と同じ。まあ一番上が楽かな……
SelfはObjectPascalから持ってきたのかなー。

cursor:move(2, 8)

cursor.move(cursor, 2, 8)

と同じ。おとなしく":"を使っておけばいいかな。


結構むずかしかったし、疲れた…… しかも割と量があるという。でもまあそれなりにおもしろかったし、いいか。
クロージャ高階関数のところでやったのでスルー。(本にもさらっと書いてあるだけだし)
これで9章が終わって次回は10章へ。