2007年 05月 02日

Lua のオブジェクト指向

できるだけ同じ名前を出さないという方向で書きかたを考えてみる。

#!/usr/bin/lua

List = {
	new = function ()
		return setmetatable({
			-- instance variables
			firstIndex = 0,
			lastIndex  = -1,
		}, { __index = List.prototype })
	end,

	prototype = {
		unshift = function (self, value)
			self.firstIndex  = self.firstIndex - 1
			self[self.firstIndex] = value
			return self
		end,

		push = function (self, value)
			self.lastIndex = self.lastIndex + 1
			self[self.lastIndex] = value
			return self
		end,

		shift = function (self)
			local first = self.firstIndex
			if first > self.lastIndex then
				return nil
			end
			local value = self[first]
			self[first] = nil
			self.firstIndex = first + 1
			return value
		end,

		pop = function (self)
			local last = self.lastIndex
			if self.firstIndex > last then
				return nil
			end
			local value = self[last]
			self[last] = nil
			self.lastIndex = last - 1
			return value
		end,

		clear = function (self)
			for i,v in ipairs(self) do
				self[i] = nil
			end
			self.firstIndex = 0
			self.lastIndex  = -1
		end,

		size  = function (self)
			return self.lastIndex - self.firstIndex + 1
		end,

		first = function (self)
			return self[self.firstIndex]
		end,

		last = function (self)
			return self[self.lastIndex]
		end,
	},
}


l = List.new()
print(l)

-- basic tests
l:push(1)
l:push(2)
assert(l:pop() == 2)
assert(l:pop() == 1)

l:push(1)
l:unshift(2)
assert(l:size()  == 2)
assert(l:first() == 2)
assert(l:last()  == 1)
assert(l:shift() == 2)
assert(l:shift() == 1)

assert(l:size() == 0)
l:push(1)
assert(l:size() == 1)
l:clear()
assert(l:size() == 0)

-- multi-obj tests
l1 = List.new()
l1:push(1)
l:push(1)
l1:push(2)
l:push(2)
assert(l1:pop() == 2)
assert(l:pop()  == 2)
assert(l1:pop() == 1)
assert(l:pop()  == 1)

-- prototype 書きかえてみる
List.prototype.hoge = function ()
	return "hoge"
end
assert(l:hoge() == "hoge")

-- プロトタイプとってきたいとき
assert(getmetatable(l).__index == List.prototype)
print "Tests are completed"

new の中ででてきてしまう。うーん。


Lua は基本的にオブジェクト指向がどうこうっていう話はあんまりなくて、テーブルとメタテーブルをごにょって自分でやるらしい。
あるテーブルはメタテーブルを持てて (そしてメタテーブルもまたテーブルなので、さらにメタテーブルを持てるはず?)、そのメタテーブルに定義された __hoge なキーの値が特別な意味をもってたりする (演算子のオーバーロードもこれでやるみたい。ためしてない)。__index キーには元のテーブルでキーが見付からないときに使われるテーブルをセットする。

self を暗黙的に使うシンタックスもあるけど、名前が何回もでてきてうざくなる。