2007年 05月 02日

Lua の OOP , クラスベース

function Class(obj)
	-- metatable も __index も同じにする (再帰参照)
	-- 演算子オーバーロードも直感的に書けるように
	obj.__index = obj
	-- superclass の設定
	setmetatable(obj, { __index = obj.super})
	return setmetatable({
		new = function(...)
			local newObj = {}
			setmetatable(newObj, obj)
			newObj:initialize(unpack(arg))
			return newObj
		end
	}, obj)
end

List = Class { super = nil,
	__tostring = function ()
		return "#<Class List>"
	end,
	
	initialize = function (self, ...)
		self.firstIndex = 0
		self.lastIndex  = -1
		for i,v in ipairs(arg) do self:push(v) end
	end,

	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)

l2 = List.new(1, 2, 3, 4, 5)
assert(l2:pop() == 5)
assert(l2:pop() == 4)
assert(l2:shift() == 1)
assert(l2:shift() == 2)
assert(l2:pop() == 3)

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

List2 = Class { super = List,
	__tostring = function ()
		return "#<Class List2>"
	end,

	initialize = function (self, values)
		self.super.initialize(self, values)
	end,

	test = function (self, str) 
		print(str)
		self:push(str)
	end
}

l2 = List2.new()
print(l2)
l2:test("hoe")
l2:push(1)
l2:push(2)
assert(l2:pop() == 2)
assert(l2:pop() == 1)

print "Tests are completed"

どうだろ