第二部分主要涉及lua的tables和objects,主要的部分包括
1.lua实现数组,链表等数据结构
2.Metatables的使用
3.全局变量_G表以及setfenv的使用
4.package的实现
5.lua如何实现类
6.weak表的应用
一,lua实现数组链表等数据结构
利用table可以很有效的实现这些数据结构,这里就不详细写
二,Metatables的使用
觉得这个也是lua一个挺有意思的特性,可以用来实现以下特性
1.定义算术操作符和关系操作符的行为
2.为 Lua 函数库提供支持,
3.控制对 table 的访问
每个一表的有一个Metatable,新建的表都是默认不带Metatable,可以新建一个表将其设置为另一个表的Metatable,当然也可以将自身设置为自身的Metatable如
t1 = {}
setmetatable(t, t1)
Metatable 能够被用于定义算术操作符和关系操作符的行为。例如:Lua 尝试对两个 table 进行加操作时,它会按顺序检查这两个 table 中是否有一个存在 metatable 并且这个 metatable 是否存在 __add 域,如果 Lua 检查到了这个 __add 域,那么会调用它,这个域被叫做 metamethod。
每个算术操作符有对应的 metamethod:
+ | __add |
* | __mul |
- | __sub |
/ | __div |
- | __unm (for negation) |
% | __mod |
^ | __pow |
同样,对于关系操作符也都有对应的 metamethod:
== | __eq |
< | __lt |
<= | __le |
其他的关系操作符都是用上面三种表示:
a ~= b 表示为 not (a == b)
a > b 表示为 b < a
a >= b 表示为 b <= a
具体例子如下:
Set = {}
Set.mt = {}
function Set.new(t)
local set = {}
for _,value in ipairs(t) do
set[value] = true
end
setmetatable(set,Set.mt)
return set
end
function Set.union (a,b)
local res = Set.new{}
for k in pairs(a) do res[k] = true end
for k in pairs(b) do res[k] = true end
return res
end
function Set.tostring (set)
local s = "{"
local sep = ""
for e in pairs(set) do
s = s .. sep .. e
sep = ", "
end
return s .. "}"
end
Set.mt.__add = Set.union
Set.mt.__tostring = Set.tostring
s1 = Set.new{10, 20, 30, 50}
s2 = Set.new{30, 1}
print(getmetatable(s1))
print(getmetatable(s2))
print(s1)
print(s2)
print(s1 + s2)
__index metamethod
在我们访问 table 的不存在的域时,Lua 会尝试调用 __index metamethod。__index metamethod 接受两个参数 table 和 key
local mt = {}
mt.__index = function(table, key)
print('table -- ' .. tostring(table))
print('key -- ' .. key)
end
local t = {}
setmetatable(t, mt)
local v = t.a
当然__index也可以是个table,当访问的时候,就好尝试访问table
local mt = {}
mt.__index = {
a = 'Hello World'
}
local t = {}
setmetatable(t, mt)
print(t.a) --> Hello World
__newindex metamethod
如果对 table 的一个不存在的域赋值时,Lua 将检查 __newindex metamethod:
1.如果 __newindex 为函数,Lua 将调用函数而不是进行赋值
2.如果 __newindex 为一个 table,Lua 将对此 table 进行赋值
如果 __newindex 为一个函数,它可以接受三个参数 table key value。如果希望忽略 __newindex 方法对 table 的域进行赋值,可以调用 rawset(t, k, v)
三.全局_G表以及setfenv的使用
全局_G
Lua用一个名为environment普通的表来保存所有的全局变量。为了简化操作,Lua将环境本身存储在一个全局变量_G中,(_G._G等于_G)。如全局变量就是放在_G表里面就好像_G["a"] = _G["var1"],这只是a = var1的复杂的写法而已。例如,下面代码打印在当前环境中所有的全局变量的名字
for n inpairs (_G) do print(n) end
setfenv
setfenv的第一个参数可以是当前的堆栈层次,如1代表当前代码块,2表调用当前的上一层,也可以是具体的那个函数名,表示在那个函数里。每个新创建的函数都将继承创建它的那个函数的全局环境
就如:
a = 1 -- create a global variable
setfenv(1, {})
print(a)
当前的环境已经设置为{}而不是之前的_G,a是定义在_G中的,所以这里再访问a就会访问失败,如果再想访问a就要
a = 1
setfenv(1, {_G = _G})
_G.print(a) --> nil
_G.print(_G.a)
在定义中重新定义_G继承老环境的_G,如果想调用老环境的时候不带_G可以这么写,用__index 附上_G
a = 1
localnewgt = {}
setmetatable(newgt, {__index = _G})
setfenv(1, newgt) -- set it
print(a) --> 1
还有段比较有意思的代码
a.lua代码
function test()
print("Test")
end
function test1()
print("Test1")
end
test()
setfenv.lua 代码
local FuncEnv={}
setmetatable(FuncEnv, {__index = _G})
local func=loadfile("a.lua")
setfenv(func,FuncEnv)() --等价于setenv(func,FuncEnv);func();
setfenv(1,FuncEnv) --这里将a.lua的test(),以及test1()也导入
test()
a = 1
print(a)
print(FuncEnv.a)
四.Package的实现
Lua并没有提供明确的机制来实现packages。然而,我们通过语言提供的基本的机制很容易实现他。主要的思想是:像标准库一样,使用表来描述package
如:
complex = {}
functioncomplex.new (r, i) return{r=r, i=i} end
complex.i = complex.new(0, 1)
这样每次调用就需要加上complex的前缀
如果想要声明一个函数只是想只可以内部访问,可以将它声明为local
localP = {}
complex = P
local functioncheckComplex (c)
.....
end
end
functionP.add (c1, c2)
checkComplex(c1);
checkComplex(c2);
.....
end
如果不想声明的时候写前缀还可以用以下方式
local function new (r, i) return {r=r, i=i} end
...
complex = {
new = new,
add = add,
sub = sub,
mul = mul,
div = div,
}
如果一个package独占一个环境还可以这么写
localP = {}
complex = P
setfenv(1, P)
function add (c1, c2)
return new(c1.r + c2.r, c1.i + c2.i)
end
这样环境就会自动转换为complex.add
五.类的实现
lua没有类,但可以通过prototype机制来实现,在这类语言中实现类(class)的机制,我们创建一个对象,作为其它对象的原型即可(原型对象为类,其它对象为类的instance)
setmetatable(a, {__index = Account})
这样,对象a调用任何不存在的成员都会到对象Account中查找。术语上,可以将Account看作类,a 看作对象
lua有机制类似于this指针,如下面的函数声明:
function Account.withdraw (self, v)
self.balance = self.balance - v
end
通过冒号声明的方式,(同时调用的时候也用冒号)来实现
function Account:withdraw (v)
self.balance = self.balance - v
end
调用时 a:withdraw(100.00)
继承问题
就是通过一个类声明一个新的对象,然后再将这个对象作为prototype,相当于声明了一个新的类。
SpecialAccount = Account:new() --Account的new用self的方式,SpecialAccount可以直接继承过来
c = SpecialAccount:new() -- 这样直接调用new也是正确的。
这样如果能在SpecialAccount能找到相应的调用就会直接调用,如果不能就继续向上在Account找
多重继续问题
多重继续就是一个类有多个父类,__index可以赋予一个函数的值,只要每次调用的时候从这个函数查找到相应的父类的调用就可以实现多重继承。
用如下函数来查找父类
local function search (k, plist)
for i=1, table.getn(plist) do
local v = plist[i][k] --尝试查找父类相应的项
if v then return v end
end
end
function createClass (...)
local c = {}
setmetatable( c, {__index = function(t, k)
return search(k, arg)
end} )
c.__index = c
-- 将c的__index 设置为search
function c: new (o)
o = o or{}
setmetatable(o, c)
return o
end
return c
end
如果还有个父类为
Named = {}
function Named:getname ()
return self.name
end
function Named:setname (n)
self.name = n
end
那么子类就可以这么声明
NamedAccount = createClass(Account, Named)
私有性:
Lua中的主要对象设计不提供私有性访问机制。部分原因因为这是我们使用通用数据结构tables来表示对象的结果。但是这也反映了后来的Lua的设计思想。Lua没有打算被用来进行大型的程序设计,相反,Lua目标定于小型到中型的程序设计,通常是作为大型系统的一部分。
不过可以通过闭包的方式来进行实现:
function newAccount (initialBalance)
local self = {balance = initialBalance}
local withdraw = function(v)
self.balance = self.balance - v
end
local deposit = function(v)
self.balance = self.balance + v
end
local getBalance = function() returnself.balance end
return{ withdraw = withdraw,
deposit = deposit,
getBalance = getBalance
}
end
self是局部变量,也只能通过返回的withdraw,deposit还有getBalance来进行访问
acc1 = newAccount(100.00)
acc1.withdraw(40.00)
print(acc1.getBalance())
单例实现方式:
前面的OO程序设计的方法有一种特殊情况:对象只有一个单一的方法。这种情况下,我们不需要创建一个接口表,取而代之的是,我们将这个单一的方法作为对象返回。
function newObject (value)
return function(action, v)
if action == "get" then return value
elseif action == "set" then value = v
else error("invalid action")
end
end
end
调用也可以这样调用
d = newObject(0)
print(d("get")) --> 0
d("set", 10)
print(d("get"))
六.weak表的使用
http://blog.csdn.net/shijihuangshou/article/details/42874423 里面另外写