1、 前言c++
lua是一种很是轻量的动态类型语言,在1993年由由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo等人发明,lua的设计目标是轻便地嵌入宿主语言,加强系统的可扩展性和可定制性。lua的源码只有两万余行,很是精简小巧,在目前的脚本引擎中,lua的速度是最快的,这也是lua进入程序设计语言前20名,现在已经普遍应用于游戏行业,这几篇文章将会讨论下lua的几个比较重要的特性。算法
一门语言的类型系统是其最根本的特征,因此本文先从与lua的类型系统关系最紧密的元表和元方法谈起。做为一门轻量级语言,lua的核心很是精简,它的基本类型只有8种:nil,boolean,number,string,userdata,function,thread和table,其中table是惟一的数据结构,是lua中最重要的类型,能够做为其余数据结构的基础,如数组,链表,队列和集合等均可以经过table实现。更强大的是,lua还为table提供了自定义操做的功能。在c++等面向对象语言中,类的可操做行为由成员函数决定。lua中,元方法就是table的“成员函数”,为不一样的table提供特殊的操做行为,元表是元方法的集合。经过元表和元方法,table能够直接实现类,继承等面向对象特性。数组
2、 元表元方法介绍数据结构
lua中每一个值其实都有元表,不过每一个table和userdata均可以有本身专有的元表,(userdata是宿主中的数据结构,可使用宿主语言的方法,为了限制过分对其使用元表,不能在lua脚本中直接设置,需经过lua_setmetatable建立,这里不讨论),而其余类型的预约义操做都在一个共享的元表中,新的table默认没有元表,必须经过setmetatable和getmetatable设置和查询元表。函数
t = {} assert(getmetatable(t)== nil) t1 = {} setmetatable(t, t1) assert(getmetatable(t) == t1)
在元表中定义的函数就是元方法,table的元方法分为算数类,关系类,库定义和访问类的元方法。ui
1. 算数类元方法lua
lua的算数类元方法都有对应的字段名,包括__add, __mul,__sub, __div,__mod和__pow等,下面示例了如何定义两个table的加法操做,spa
a = { "a1", "a2","a3" } b = { "b1", "b2", "b3" } meta = { } meta.__add = function(t1, t2) t = { } for k, v in ipairs(t1) do table.insert(t, v) end for k, v in ipairs(t2) do table.insert(t, v) end return t end setmetatable(a, meta) c = a+b for _,v in ipairs(c) do print(v) end
上面代码中只须要给表a设置了元表,表b没有元表也能正常运行,这与lua查找元表的顺序有关系。lua先查找第一个table,若是有元表而且其中有 __add方法就调用该方法,不关心第二个table有没有元表;不然查找第二个table有没有__add的元方法,有就调用第二个table的元方法;若是都没有这个元方法就引起一个错误。设计
2. 关系类元方法code
关系类元方法只有等于__eq,小于__lt和小于等于__le这3个操做,其余3个会自动转化,如a>b会自动转为b<a.
a = { "a1", "a2" } b = { "b1", "b2", "b3" } meta = { } meta.__le = function(a, b) for k in pairs(a) do if not b[k] then return false end end return true end meta.__lt = function(a, b) return a<=b and not (b<=a) end meta.__eq = function(a, b) return a<=b and b<=a end setmetatable(a, meta) setmetatable(b, meta) print(a<=b) print(a<b) print(a>=a) print(b>a) print(b<a)
与算法类元方法不一样,table必须具备相同的元方法才能用于比较操做。
3. 库定义的元方法
上面的元方法都是lua核心具备的,是lua虚拟机定义的,除此以外,各类程序库也会用本身的字段定义元方法,好比print老是调用table的tostring方法,
a = { "a1", "a2" } meta.__tostring = function(a) local l = { } for _,k in pairs(a) do l[#l+1] = k; end return "{"..table.concat(l, ",").."}" end setmetatable(a, meta) print(a)
4. 访问类元方法
访问元方法使用最广泛的是__index和__newindex。通常当访问一个table中不存在的元素时会返回nil,可是若是table具备__index元方法,就不返回nil而是调用这个元方法。利用__index能够方便地实现继承,
mt = { } mt.__index = function(t, k) return base[k] end base = { b1 = 1, b2 = 2, b3 = 3 } derive = { d = 4 } setmetatable(derive, mt) print(derive.b1) print(derive.d)
当对table中不存在的索引赋值时就会调用__newindex元方法,
mt = { } mt.__newindex = function(t, k, v) base[k] = v end base = { b1 = 1, b2 = 2, b3 = 3 } derive = { d1 = 4 } setmetatable(derive, mt) derive["d2"] = 5 print(base.d2)
3、 元表元方法实例
下面是一个使用元方法的实例,用于产生迭代递增表,
T = { container = { } } T.mt = { __add = function(a, b) local c = T.new{} for k,v in pairs(T.new(a)) do c[k] = v end for k,v in pairs(T.new(b)) do c[k] = v end return c end, __sub = function(a, b) local c = T.new{} for k,v in pairs(T.new(a)) do c[k] = v end for k,v in pairs(T.new(b)) do c[k] = nil end return c end, __tostring = function(a) local l = { } for k in pairs(a) do l[#l+1] = k; end return "{"..table.concat(l, ",").."}" end } T.new = function(t) if (t == nil) then t = {} end if (getmetatable(t) == T.mt) then return t end local r = {} for _, b in ipairs(t) do r[tostring(b)] = true end setmetatable(r, T.mt) return r end T.print = function(t) for k, v in pairs(t.container) do print(k) print(v) end end local mt = { __newindex = function(t, k, v) t.container[k] = T.new(v) end, __index = function(t, k) return t.container[k] end, } setmetatable(T, mt) T["first"] = { "a1", "b1"} print("elements in table first") T.print(T) T["second"] = T["first"] + { "a2", "b2", "a3", "b3"} print("elements in table first and second") T.print(T) T["third"] = T["second"] - { "a3", "b3" } print("elements in table first, second and third") T.print(T)