一、Table
Table实际上是数组和哈希表的混合体。当保存以连续整数为索引的内容时,使用数组结构;而对于离散索引及键值对,则使用哈希表存储。这样做对于空间节省和查找效率都是有好处的,举例:
tt = {"a" , "b" , "c"}
print(#tt) --> 3
tt[100] = "d"
print(#tt) --> 3 (100-"d"键值对放入哈希表)
tt[4] = "e"
print(#tt) --> 4
二、Closure
Lua的变量引用规则造成了闭包的出现,在Lua中一个复杂数据结构(table、function)被赋予一个变量时,变量仅仅拿取其引用。试考虑下面代码片段:
local f1 = function() ... end
local f2 = function() ... end
local f3 = f1
判断f1与f2是否相等,得到的结果为假,而f3与f1则为真。这是因为f1与f2引用了两个不同的函数chunk(也可称为“定义”),而f1直接将其引用(可以看成C/C++中的地址)赋给f3,故而相等。
理解了上面问题,再看看下面的一个函数:
function createCounter()
local x = 0
return function()
x = x + 1
print(x)
end
end
那么,下面c1与c2相等吗?
c1 = createCounter()
c2 = createCounter()
由于createCounter中的return部分创建了一个新函数,因此c1和c2引用的函数并不相同。因为return的匿名函数中使用到了外部函数(即createCounter)的局部变量x,所以Lua必须要在某个地方保存这个变量,又因为每次返回的都是一个新函数,所以Lua不能共享createCounter中的x,进而Lua必须为每个新函数分配一个保存x的地方,这个问题导致了闭包概念的产生。
通过上面分析,可以明白是语言机制导致了闭包,而绝非Lua作者故意制造出这么一个看似有点神秘的东西。
下面给出闭包的定义:一个保留了创建时上下文的函数。而upvalue的定义是:被闭包访问的保留变量。实际上所有在Lua中创建的函数都是闭包,在许多情况下仅有一个原型的闭包存在,因为函数块只执行了一次(也可以说定义了一次)。
下面代码用闭包实现了一个对象工厂:
local function CounterFactory()
local x = 0
return
{
Increase = function()
x = x + 1
end ,
Decrease = function()
x = x - 1
end ,
GetValue = function()
return x
end
}
end
local c1 = CounterFactory()
local c2 = CounterFactory()
c1.Increase()
c1.Increase()
print(c1.GetValue())
三、Tail Call
首先需要明确What is ‘Tail Call’?——其形式为“return f(…)”,return语句中的函数调用仅限于单一调用,涉及函数返回值运算的复合表达式不是尾调用,如:return i * f(i - 1)。由定义可以看出尾调用实际上是对位于返回语句中的函数调用的优化。通常调用一个函数都需要将其上下文入栈,然后再进入被调函数。在Lua中执行符合尾调用形式的函数时,因为该函数已经没有代码需要执行了,故作者作了不保存上下文的优化。这样带来的好处是:对于精心构建的递归调用,不需要有栈的消耗,因而绝对不会出现overflow,看看下面递归调用产生的死循环:
local function f(n)
local i = n + 1
print(i)
return f(i) -- tail call
end
f(0)
四、Metatable
Metatable常用来扩展table的行为,通过setmetatable(table , metatable)将metatable挂接到table上,很多开发者用该特性来实现面向对象的设计。Metatable中提供了Metamethods,这些方法都以双下划线__开头,如:
__add , __call , __concat , __div , __eq , __index...
以上Metamethods中,__index被调用的条件是:key不在对应的table中。利用该特性可以实现类-对象、继承等面向对象概念,下面是个简单例子:
local Actor = {}
Actor.name = "unnamed"
Actor.hp = 100
Actor.dead = false
Actor.level = 1
Actor.type = "unknown"
function Actor:Say(text)
print(self.name..":"..text)
end
function Actor:IsDead()
return self.dead
end
local AttackableActor = setmetatable({ } , { __index = Actor })
AttackableActor.damage = 20
function AttackableActor:ReceiveHit(attacker)
self:Say("I'm being attacked by "..attacker.name)
self.hp = self.hp - attacker.damage
if self.hp <= 0 then
self:Say("I'm died :(")
self.dead = true
else
self:Say("My HP is "..self.hp)
end
end
function AttackableActor:Attack(target)
if target.dead == true then
self:Say("Oh, "..target.name.." has been dead!")
else
self:Say("I'm is attacking "..target.name)
target:ReceiveHit(self)
end
end
function CreateMonster(name , damage)
local m = setmetatable({ } , { __index = AttackableActor})
m.name = name
m.damage = damage
m.type = "monster"
return m
end
function CreatePlayer(name , damage)
local p = setmetatable({ } , { __index = AttackableActor})
p.name = name
p.damage = damage
p.type = "player"
return p
end
math.randomseed(os.time())
local boss = CreateMonster("Kerrigan" , math.floor(100 * math.random()))
local player = CreatePlayer("Raynor" , math.floor(100 * math.random()))
while player:IsDead() == false and boss:IsDead() == false do
player:Attack(boss)
boss:Attack(player)
end
五、Performance Tips
请参考Lua Performance Tips
六、总结
Lua是一门语法简洁,用法灵活的动态语言。在全面掌握之后,需悉心使用乃可构造出简洁高效的代码。