您的位置:首页 > 编程语言 > Lua

Lua简易入门

2015-01-12 09:45 309 查看


Lua脚本语法说明(修订)

Lua脚本语法说明(增加lua5.1部份特性)

Lua 的语法比较简单,学习起来也比较省力,但功能却并不弱。

所以,我只简单的归纳一下Lua的一些语法规则,使用起来方便好查就可以了。估计看完了,就懂得怎么写Lua程序了。

在Lua中,一切都是变量,除了关键字。

I. 首先是注释

写一个程序,总是少不了注释的。

在Lua中,你可以使用单行注释和多行注释。

单行注释中,连续两个减号"--"表示注释的开始,一直延续到行末为止。相当于C++语言中的"//"。

多行注释中,由"--[["表示注释开始,并且一直延续到"]]"为止。这种注释相当于C语言中的"/*...*/"。在注释当中,"[["和"]]"是可以嵌套的(在lua5.1中,中括号中间是可以加若干个"="号的,如 [==[ ... ]==]),见下面的字符串表示说明。

II. Lua编程

经典的"Hello world"的程序总是被用来开始介绍一种语言。在Lua中,写一个这样的程序很简单:

print("Hello world")

在Lua中,语句之间可以用分号";"隔开,也可以用空白隔开。一般来说,如果多个语句写在同一行的话,建议总是用分号隔开。

Lua 有好几种程序控制语句,如:

控制语句 格式 示例

If if 条件 then ... elseif 条件 then ... else ... end if 1+1=2 then print("true")

elseif 1+2~=3 then print("true")

else print("false") end

While while 条件 do ... end while 1+1~=2 do print("true") end

Repeat repeat ... until 条件 repeat print("Hello") until 1+1~=2

For for 变量=初值, 终点值, 步进 do ... end for i = 1, 10, 2 do print(i) end

For for 变量1, 变量2, ... 变量n in 表或枚举函数 do ... end for a,b in mylist do print(a, b) end

注意一下,for的循环变量总是只作用于for的局部变量;当省略步进值时,for循环会使用1作为步进值。

使用break可以用来中止一个循环。

相对C语言来说,Lua有几个地方是明显不同的,所以面要特别注意一下:

.语句块

语句块在C中是用"{"和"}"括起来的,在Lua中,它是用do 和 end 括起来的。比如:

do print("Hello") end

可以在 函数 中和 语句块 中定局部变量。

.赋值语句

赋值语句在Lua被强化了。它可以同时给多个变量赋值。

例如:

a,b,c,d=1,2,3,4

甚至是:

a,b=b,a -- 多么方便的交换变量功能啊。

在默认情况下,变量总是认为是全局的。假如需要定义局部变量,则在第一次赋值的时候,需要用local说明。比如:

local a,b,c = 1,2,3 -- a,b,c都是局部变量

.数值运算

和C语言一样,支持 +, -, *, /。但Lua还多了一个"^"。这表示指数乘方运算。比如2^3 结果为8, 2^4结果为16。

连接两个字符串,可以用".."运处符。如:

"This a " .. "string." -- 等于 "this a string"

.比较运算

比较符号 < > <= >= == ~=

含义 小于 大于 小于或等于 大于或等于 相等 不相等

所有这些操作符总是返回true或false。

对于Table,Function和Userdata类型的数据,只有 == 和 ~=可以用。相等表示两个变量引用的是同一个数据。比如:

a={1,2}

b=a

print(a==b, a~=b) --输出 true, false

a={1,2}

b={1,2}

print(a==b, a~=b) --输出 false, true

.逻辑运算

and, or, not

其中,and 和 or 与C语言区别特别大。

在这里,请先记住,在Lua中,只有false和nil才计算为false,其它任何数据都计算为true,0也是true!

and 和 or的运算结果不是true和false,而是和它的两个操作数相关。

a and b:如果a为false,则返回a;否则返回b

a or b:如果 a 为true,则返回a;否则返回b

举几个例子:

print(4 and 5) --输出 5

print(nil and 13) --输出 nil

print(false and 13) --输出 false

print(4 or 5) --输出 4

print(false or 5) --输出 5

在Lua中这是很有用的特性,也是比较令人混洧的特性。

我们可以模拟C语言中的语句:x = a? b : c,在Lua中,可以写成:x = a and b or c。

最有用的语句是: x = x or v,它相当于:if not x then x = v end 。

.运算符优先级,从低到高顺序如下:

or

and

< > <= >= ~= ==

.. (字符串连接)

+ -

* / %

not #(lua5.1 取长度运算) - (一元运算)

^

和C语言一样,括号可以改变优先级。

III. 关键字

关键字是不能做为变量的。Lua的关键字不多,以下几个:

and break do else elseif

end false for function if

in local nil not or

repeat return then true until while

IV. 变量类型

  怎么确定一个变量是什么类型的呢?大家可以用type()函数来检查。Lua支持的类型有以下几种:

Nil 空值,所有没有使用过的变量,都是nil。nil既是值,又是类型。

Boolean 布尔值,只有两个有效值:true和false

Number 数值,在Lua里,数值相当于C语言的double

String 字符串,如果你愿意的话,字符串是可以包含"\0"字符的(这和C语言总是以"\0"结尾是不一样的)

Table 关系表类型,这个类型功能比较强大,请参考后面的内容。

Function 函数类型,不要怀疑,函数也是一种类型,也就是说,所有的函数,它本身就是一个变量。

Userdata 嗯,这个类型专门用来和Lua的宿主打交道的。宿主通常是用C和C++来编写的,在这种情况下,Userdata可以是宿主的任意数据类型,常用的有Struct和指针。

Thread 线程类型,在Lua中没有真正的线程。Lua中可以将一个函数分成几部份运行。如果感兴趣的话,可以去看看Lua的文档。

现在回过头来看看,倒觉得不是线程类型。反而象是用来做遍历的,象是Iterator函数。

如:

function range(n)

local i = 0

while(i < n) do

coroutine.yield( i )

i = i + 1

end

end

可惜的是要继续运行,需要coroutine.resume函数,有点鸡肋。请指教。

V. 变量的定义

  所有的语言,都要用到变量。在Lua中,不管在什么地方使用变量,都不需要声明,并且所有的这些变量总是全局变量,除非我们在前面加上"local"。这一点要特别注意,因为我们可能想在函数里使用局部变量,却忘了用local来说明。

至于变量名字,它是大小写相关的。也就是说,A和a是两个不同的变量。

定义一个变量的方法就是赋值。"="操作就是用来赋值的

我们一起来定义几种常用类型的变量吧。

A. Nil

    正如前面所说的,没有使用过的变量的值,都是Nil。有时候我们也需要将一个变量清除,这时候,我们可以直接给变量赋以nil值。如:

var1=nil -- 请注意 nil 一定要小写

B. Boolean

    布尔值通常是用在进行条件判断的时候。布尔值有两种:true 和 false。在Lua中,只有false和nil才被计算为false,而所有任何其它类型的值,都是true。比如0,空串等等,都是true。不要被 C语言的习惯所误导,0在Lua中的的确确是true。你也可以直接给一个变量赋以Boolean类型的值,如:

theBoolean = true

C. Number

    在Lua中,是没有整数类型的,也不需要。一般情况下,只要数值不是很大(比如不超过100,000,000,000,000),是不会产生舍入误差的。在WindowsXP能跑的当今主流PC上,实数的运算并不比整数慢。

实数的表示方法,同C语言类似,如:

4 0.4 4.57e-3 0.3e12 5e+20

D. String

字符串,总是一种非常常用的高级类型。在Lua中,我们可以非常方便的定义很长很长的字符串。

字符串在Lua中有几种方法来表示,最通用的方法,是用双引号或单引号来括起一个字符串的,如:

"That's go!"



'Hello world!'

和C语言相同的,它支持一些转义字符,列表如下:

\a bell

\b back space

\f form feed

\n newline

\r carriage return

\t horizontal tab

\v vertical tab

\\ backslash

\" double quote

\" single quote

\[ left square bracket

\] right square bracket

由于这种字符串只能写在一行中,因此,不可避免的要用到转义字符。加入了转义字符的串,看起来实在是不敢恭维,比如:

"one line\nnext line\n\"in quotes\", "in quotes""

一大堆的"\"符号让人看起来很倒胃口。如果你与我有同感,那么,我们在Lua中,可以用另一种表示方法:用"[["和"]]"将多行的字符串括起来。(lua5.1: 中括号中间可以加入若干个"="号,如 [==[ ... ]==],详见下面示例)

示例:下面的语句所表示的是完全相同的字符串:

a = 'alo\n123"'

a = "alo\n123\""

a = '\97lo\10\04923"'

a = [[alo

123"]]

a = [==[

alo

123"]==]

值得注意的是,在这种字符串中,如果含有单独使用的"[["或"]]"就仍然得用"\["或"\]"来避免歧义。当然,这种情况是极少会发生的。

 E. Table

    关系表类型,这是一个很强大的类型。我们可以把这个类型看 作是一个数组。只是C语言的数组,只能用正整数来作索引;在Lua中,你可以用任意类型来作数组的索引,除了nil。同样,在C语言中,数组的内容只允许 一种类型;在Lua中,你也可以用任意类型的值来作数组的内容,除了nil。

Table的定义很简单,它的主要特征是用"{"和"}"来括起一系列数据元素的。比如:

T1 = {} -- 定义一个空表

T1[1]=10 -- 然后我们就可以象C语言一样来使用它了。

T1["John"]={Age=27, Gender="Male"}

这一句相当于:

T1["John"]={} -- 必须先定义成一个表,还记得未定义的变量是nil类型吗

T1["John"]["Age"]=27

T1["John"]["Gender"]="Male"

当表的索引是字符串的时候,我们可以简写成:

T1.John={}

T1.John.Age=27

T1.John.Gender="Male"



T1.John{Age=27, Gender="Male"}

这是一个很强的特性。

在定义表的时候,我们可以把所有的数据内容一起写在"{"和"}"之间,这样子是非常方便,而且很好看。比如,前面的T1的定义,我们可以这么写:

T1=

{

10, -- 相当于 [1] = 10

[100] = 40,

John= -- 如果你原意,你还可以写成:["John"] =

{

Age=27, -- 如果你原意,你还可以写成:["Age"] =27

Gender=Male -- 如果你原意,你还可以写成:["Gender"] =Male

},

20 -- 相当于 [2] = 20

}

看起来很漂亮,不是吗?我们在写的时候,需要注意三点:

第一,所有元素之间,总是用逗号","隔开;

第二,所有索引值都需要用"["和"]"括起来;如果是字符串,还可以去掉引号和中括号;

第三,如果不写索引,则索引就会被认为是数字,并按顺序自动从1往后编;

表类型的构造是如此的方便,以致于常常被人用来代替配置文件。是的,不用怀疑,它比ini文件要漂亮,并且强大的多。

  F. Function

函数,在Lua中,函数的定义也很简单。典型的定义如下:

function add(a,b) -- add 是函数名字,a和b是参数名字

return a+b -- return 用来返回函数的运行结果

end

请注意,return语言一定要写在end之前。假如我们非要在中间放上一句return,那么就应该要写成:do return end。

还记得前面说过,函数也是变量类型吗?上面的函数定义,其实相当于:

add = function (a,b) return a+b end

当重新给add赋值时,它就不再表示这个函数了。我们甚至可以赋给add任意数据,包括nil (这样,赋值为nil,将会把该变量清除)。Function是不是很象C语言的函数指针呢?

和C语言一样,Lua的函数可以接受可变参数个数,它同样是用"..."来定义的,比如:

function sum (a,b, )

如果想取得...所代表的参数,可以在函数中访问arg局部变量(表类型)得到 (lua5.1: 取消arg,并直接用"..."来代表可变参数了,本质还是arg)。

如 sum(1,2,3,4)

则,在函数中,a = 1, b = 2, arg = {3, 4} (lua5.1: a = 1, b = 2, ... = {3, 4})

更可贵的是,它可以同时返回多个结果,比如:

function s()

return 1,2,3,4

end

a,b,c,d = s() -- 此时,a = 1, b = 2, c = 3, d = 4

前面说过,表类型可以拥有任意类型的值,包括函数!因此,有一个很强大的特性是,拥有函数的表,哦,我想更恰当的应该说是对象吧。Lua可以使用面向对象编程了。不信?举例如下:

t =

{

Age = 27

add = function(self, n) self.Age = self.Age+n end

}

print(t.Age) -- 27

t.add(t, 10)

print(t.Age) -- 37

不过,t.add(t,10) 这一句实在是有点土对吧?没关系,在Lua中,我们可以简写成:

t:add(10) -- 相当于 t.add(t,10)

G. Userdata 和 Thread

这两个类型的话题,超出了本文的内容,就不打算细说了。

VI. 结束语

  就这么结束了吗?当然不是,接下来,我们需要用Lua解释器,来帮助理解和实践了。相信这样会更快的对Lua上手了。

就象C语言一样,Lua提供了相当多的标准函数来增强语言的功能。使用这些标准函数,可以很方便的操作各种数据类型,并处理输入输出。有关这方面的信息,我们可以参考《Programming in Lua 》一书,也可以在网络上直接观看电子版,网址为:http://www.lua.org/pil/index.html

  

备注:本文的部份内容摘、译自lua随机文档。

相关链接:

1. Lua 官方网站: http://www.lua.org

2. Lua Wiki网站,你可以在这里找到很多相关的资料,如文档、教程、扩展,以及C/C++的包装等: http://lua-users.org/wiki/

浅析Lua中table的遍历和删除(转)



当我在工作中使用lua进行开发时,发现在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式不同,这四种方式分别是:
for key, value in pairs(tbtest) do
XXX
end

for key, value in ipairs(tbtest) do
XXX
end

for i=1, #(tbtest) do
XXX
end

for i=1, table.maxn(tbtest) do
XXX
end


前两种是泛型遍历,后两种是数值型遍历。当然你还会说lua的table遍历还有很多种方法啊,没错,不过最常见的这些遍历确实有必要弄清楚。

这四种方式各有特点,由于在工作中我几乎每天都会使用遍历table的方法,一开始也非常困惑这些方式的不同,一段时间后才渐渐明白,这里我也是把自己的一点经验告诉大家,对跟我一样的lua初学者也许有些帮助(至少当初我在写的时候在网上就找了很久,不知道是因为大牛们都认为这些很简单,不需要说,还是因为我笨,连这都要问)。

首先要明确一点,就是lua中table并非像是C/C++中的数组一样是顺序存储的,准确来说lua中的table更加像是C++中的map,通过Key对应存储Value,但是并非顺序来保存key-value对,而是使用了hash的方式,这样能够更加快速的访问key对应的value,我们也知道hash表的遍历需要使用所谓的迭代器来进行,同样,lua也有自己的迭代器,就是上面4种遍历方式中的pairs和ipairs遍历。但是lua同时提供了按照key来遍历的方式(另外两种,实质上是一种),正式因为它提供了这种按key的遍历,才造成了我一开始的困惑,我一度认为lua中关于table的遍历是按照我table定义key的顺序来的。

下面依次来讲讲四种遍历方式,首先来看for k,v in pairs(tbtest) do这种方式:

先看效果:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
[4] = 4,
}

for key, value in pairs(tbtest) do
print(value)
end


我认为输出应该是1,2,3,4,实际上的输出是1,2,4,3。我因为这个造成了一个bug,这是后话。

也就是说for k,v in pairs(tbtest) do 这样的遍历顺序并非是tbtest中table的排列顺序,而是根据tbtest中key的hash值排列的顺序来遍历的。

当然,同时lua也提供了按照key的大小顺序来遍历的,注意,是大小顺序,仍然不是key定义的顺序,这种遍历方式就是for k,v in ipairs(tbtest) do。

for k,v in ipairs(tbtest) do 这样的循环必须要求tbtest中的key为顺序的,而且必须是从1开始,ipairs只会从1开始按连续的key顺序遍历到key不连续为止。
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
[5] = 5,
}

for k,v in ipairs(tbtest) do
print(v)
end


只会打印1,2,3。而5则不会显示。
local tbtest = {
[2] = 2,
[3] = 3,
[5] = 5,
}

for k,v in ipairs(tbtest) do
print(v)
end


这样就一个都不会打印。

第三种遍历方式有一种神奇的符号'#',这个符号的作用是是获取table的长度,比如:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
}
print(#(tbtest))


打印的就是3
tbtest = {
[1] = 1,
[2] = 2,
[6] = 6,
}
print(#(tbtest))


这样打印的就是2,而且和table内的定义顺序没有关系,无论你是否先定义的key为6的值,‘#’都会查找key为1的值开始。

如果table的定义是这样的:
tbtest = {
["a"] = 1,
[2] = 2,
[3] = 3,
}

print(#(tbtest))


那么打印的就是0了。因为‘#’没有找到key为1的值。同样:
tbtest = {
[“a”] = 1,
[“b”] = 2,
[“c”] = 3,
}
print(#(tbtest))


打印的也是0

所以,for i=1, #(tbtest) do这种遍历,只能遍历当tbtest中存在key为1的value时才会出现结果,而且是按照key从1开始依次递增1的顺序来遍历,找到一个递增不是1的时候就结束不再遍历,无论后面是否仍然是顺序的key,比如:

table.maxn获取的只针对整数的key,字符串的key是没办法获取到的,比如:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
}
print(table.maxn(tbtest))

tbtest = {
[6] = 6,
[1] = 1,
[2] = 2,
}
print(table.maxn(tbtest))


这样打印的就是3和6,而且和table内的定义顺序没有关系,无论你是否先定义的key为6的值,table.maxn都会获取整数型key中的最大值。

如果table的定义是这样的:
tbtest = {
["a"] = 1,
[2] = 2,
[3] = 3,
}
print(table.maxn(tbtest))


那么打印的就是3了。如果table是:
tbtest = {
[“a”] = 1,
[“b”] = 2,
[“c”] = 3,
}
print(table.maxn(tbtest))
print(#(tbtest))


那么打印的就全部是0了。

换句话说,事实上因为lua中table的构造表达式非常灵活,在同一个table中,你可以随意定义各种你想要的内容,比如:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
["a"] = 4,
["b"] = 5,
}


同时由于这个灵活性,你也没有办法获取整个table的长度,其实在coding的过程中,你会发现,你真正想要获取整个table长度的地方几乎没有,你总能采取一种非常巧妙的定义方式,把这种需要获取整个table长度的操作避免掉,比如:
tbtest = {
tbaaa = {
[1] = 1,
[2] = 2,
[3] = 3,
},
["a"] = 4,
["b"] = 5,
}


你可能会惊讶,上面这种table该如何遍历呢?
for k, v in pairs(tbtest) do
print(k, v)
end


输出是:a 4 b 5 tbaaa table:XXXXX。

由此你可以看到,其实在table中定义一个table,这个table的名字就是key,对应的内容其实是table的地址。

当然,如果你用
for k, v in ipairs(tbtest) do
print(k,v)
end


来遍历的话,就什么都不会打印,因为没有key为1的值。但当你增加一个key为1的值时,ipairs只会打印那一个值,现在你明白ipairs是如何工作的吧。

既然这里谈到了遍历,就说一下目前看到的几种针对table的遍历方式:

for i=1, #tbtest do --这种方式无法遍历所有的元素,因为'#'只会获取tbtest中从key为1开始的key连续的那几个元素,如果没有key为1,那么这个循环将无法进入

for i=1, table.maxn(tbtest) do --这种方式同样无法遍历所有的元素,因为table.maxn只会获取key为整数中最大的那个数,遍历的元素其实是查找tbtest[1]~tbtest[整数key中最大值],所以,对于string做key的元素不会去查找,而且这么查找的效率低下,因为如果你整数key中定义的最大的key是10000,然而10000以下的key没有几个,那么这么遍历会浪费很多时间,因为会从1开始直到10000每一个元素都会查找一遍,实际上大多数元素都是不存在的,比如:
tbtest = {
[1] = 1,
[10000] = 2,
}
local count = 0
for i=1, table.maxn(tbtest) do
count = count + 1
print(tbtest[i])
end
print(count)


你会看到打印结果是多么的坑爹,只有1和10000是有意义的,其他的全是nil,而且count是10000。耗时非常久。一般我不这么遍历。但是有一种情况下又必须这么遍历,这个在我的工作中还真的遇到了,这是后话,等讲完了再谈。
for k, v in pairs(tbtest) do


这个是唯一一种可以保证遍历tbtest中每一个元素的方式,别高兴的太早,这种遍历也有它自身的缺点,就是遍历的顺序不是按照tbtest定义的顺序来遍历的,这个前面讲到过,当然,对于不需要顺序遍历的用法,这个是唯一可靠的遍历方式。
for k, v in ipairs(tbtest) do


这个只会遍历tbtest中key为整数,而且必须从1开始的那些连续元素,如果没有1开始的key,那么这个遍历是无效的,我个人认为这种遍历方式完全可以被改造table和for i=1, #(tbtest) do的方式来代替,因为ipairs的效果和'#'的效果,在遍历的时候是类似的,都是按照key的递增1顺序来遍历。

好,再来谈谈为什么我需要使用table.maxn这种非常浪费的方式来遍历,在工作中, 我遇到一个问题,就是需要把当前的周序,转换成对应的奖励,简单来说,就是从一个活动开始算起,每周的奖励都不是固定的,比如1~4周给一种奖励,5~8周给另一种奖励,或者是一种排名奖励,1~8名给一种奖励,9~16名给另一种奖励,这种情况下,我根据长久的C语言的习惯,会把table定义成这个样子:
tbtestAward = {
[8] = 1,
[16] = 3,
}


这个代表,1~8给奖励1,9~16给奖励3。这样定义的好处是奖励我只需要写一次(这里的奖励用数字做了简化,实际上奖励也是一个大的table,里面还有非常复杂的结构)。然后我就遇到一个问题,即我需要根据周序数,或者是排名序数来确定给哪一种奖励,比如当前周序数是5,那么我应该给我定义好的key为8的那一档奖励,或者当前周序数是15,那么我应该给奖励3。由此读者看出,其实我定义的key是一个分界,小于这个key而大于上一个key,那么就给这个key的奖励,这就是我判断的条件。逻辑上没有问题,但是lua的遍历方式却把我狠狠地坑了一把。读者可以自己想一想我上面介绍的4种遍历方式,该用哪一种来实现我的这种需求呢?这个函数的大致框架如下:
function GetAward(nSeq)
for 遍历整个奖励表 do
if 满足key的条件 then
return 返回对应奖励的key
end
end
return nil
end


我也不卖关子了,分别来说一说吧,首先因为我的key不是连续的,而且没有key为1的值,所以ipairs和'#'遍历是没用的。这种情况下理想的遍历貌似是pairs,因为它会遍历我的每一个元素,但是读者不要忘记了,pairs遍历并非是按照我定义的顺序来遍历,如果我真的使用的条件是:序数nSeq小于这个key而大于上一个key,那么就返回这个key。那么我无法保证程序执行的正确性,因为key的顺序有可能是乱的,也就是有可能先遍历到的是key为16的值,然后才是key为8的值。

这么看来我只剩下table.maxn这么一种方式了,于是我写下了这种代码:
for i=1, table.maxn(tbtestAward) do
if tbtestAward[i] ~= nil then
if nSeq <= i then
return i
end
end
end


这么写效率确实低下,因为实际上还是遍历了从key为1开始直到key为table.maxn中间的每一个值,不过能够满足我上面的要求。当时我是这么实现的,因为这个奖励表会不断的发生变化,这样我每次修改只需要修改这个奖励表就能够满足要求了,后来我想了想,觉得其实我如果自己再定义一个序数转换成对应的奖励数种类的表就可以避免这种坑爹的操作了,不过如果奖励发生修改,我需要统一排查的地方就不止这个奖励表了,权衡再三,我还是没有改,就这么写了。没办法,不断变化的需求已经把我磨练的忘记了程序的最高理想。我甚至愿意牺牲算法的效率而去追求改动的稳定性。在此哀悼程序员的无奈。我这种时间换空间的做法确实不知道好不好。

后来我在《Programming In Lua》中看到了一个神奇的迭代器,使用它就可以达到我想要的这种遍历方式,而且不需要去遍历那些不存在的key。它的方法是把你所需要遍历的table里的key按照遍历顺序放到另一个临时的table中去,这样只需要遍历这个临时的table按顺序取出原table中的key就可以了。如下:

首先定义一个迭代器:
function pairsByKeys(t)
local a = {}
for n in pairs(t) do
a[#a+1] = n
end
table.sort(a)
local i = 0
return function()
i = i + 1
return a[i], t[a[i]]
end
end


然后在遍历的时候使用这个迭代器就可以了,table同上,遍历如下:
for key, value in pairsByKeys(tbtestAward) do
if nSeq <= key then
return key
end
end


并且后来我发现有了这个迭代器,我根本不需要先做一步获取是哪一档次的奖励的操作,直接使用这个迭代器进行发奖就可以了。大师就是大师,我怎么就没想到呢!

还有些话我还没有说,比如上面数值型遍历也并非是像看起来那样进行遍历的,比如下面的遍历:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
[5] = 5,
}

for i=1, #(tbtest) do
print(tbtest[i])
end


打印的顺序是:1,2,3。不会打印5,因为5已经不在table的数组数据块中了,我估计是被放到了hash数据块中,但是当我修改其中的一些key时,比如:
tbtest = {
[1] = 1,
[2] = 2,
[4] = 4,
[5] = 5,
}

for i=1, #(tbtest) do
print(tbtest[i])
end


打印的内容却是:1,2,nil,4,5。这个地方又遍历到了中间没有的key值,并且还能继续遍历下去。我最近正在看lua源码中table的实现部分,已经明白了是怎么回事,不过我想等我能够更加清晰的阐述lua中table的实现过程了再向大家介绍。用我师傅的话说就是不要使用一些未定义的行为方法,避免在工作中出错,不过工作外,我还是希望能明白未定义的行为中那些必然性,o(︶︿︶)o 唉!因果论的孩子伤不起。等我下一篇博文分析lua源码中table的实现就能够更加清晰的说明这些了。

---------------------------------------------------------------------------------------------分割线-----------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------分割线-----------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------分割线-----------------------------------------------------------------------------------------------------------

原文 http://childhood.logdown.com/posts/200499/lua-table-how-to-safely-remove-an-element

在Lua中,table如何安全的移除元素这点挺重要,因为如果不小心,会没有正确的移除,造成内存泄漏。
[title3]
引子[/title3]

比如有些朋友常常这么做,大家看有啥问题
将test表中的偶数移除掉
<span class="kd">local</span> <span class="n">test</span> <span class="o">=</span> <span class="p">{</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">2</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">3</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">4</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">8</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">9</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">100</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">20</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">13</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">15</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">7</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">11</span></span><span class="p">}</span>
<span class="k"><span class="keyword" style="font-weight:bold">for</span></span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="k"><span class="keyword" style="font-weight:bold">in</span></span> <span class="nb">ipairs</span><span class="p">(</span> <span class="n">test</span> <span class="p">)</span> <span class="k"><span class="keyword" style="font-weight:bold">do</span></span>
<span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">if</span></span> <span class="n">v</span> <span class="o">%</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">2</span></span> <span class="o">==</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">0</span></span> <span class="k"><span class="keyword" style="font-weight:bold">then</span></span>
<span class="indent">  </span><span class="indent">  </span><span class="nb">table.remove</span><span class="p">(</span><span class="n">test</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
<span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>

<span class="k"><span class="keyword" style="font-weight:bold">for</span></span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="k"><span class="keyword" style="font-weight:bold">in</span></span> <span class="nb">ipairs</span><span class="p">(</span> <span class="n">test</span> <span class="p">)</span> <span class="k"><span class="keyword" style="font-weight:bold">do</span></span>
<span class="indent">  </span><span class="nb">print</span><span class="p">(</span><span class="n">i</span> <span class="o">..</span> <span class="s2"><span class="string" style="color:rgb(221,17,68)">"===="</span></span> <span class="o">..</span> <span class="n">v</span><span class="p">)</span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>


打印结果:
<span class="mi">1</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">3</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">2</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">8</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">3</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">9</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">4</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">20</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">5</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">13</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">6</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">15</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">7</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">7</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">8</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">11</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="p"><span class="title" style="color:rgb(153,0,0); font-weight:bold">[</span></span><span class="title" style="color:rgb(153,0,0); font-weight:bold"><span class="n">Finished</span> <span class="k">in</span> <span class="mi">0</span><span class="p">.</span><span class="mi">0</span><span class="n">s</span><span class="p">]</span></span><span class="p"></span>


有问题吧,20怎么还在?这就是在遍历中删除导致的。
[title3]
如何做呢?[/title3]

Let's get started!
<span class="kd">local</span> <span class="n">test</span> <span class="o">=</span> <span class="p">{</span> <span class="s1"><span class="string" style="color:rgb(221,17,68)">'a'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'b'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'c'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'d'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'e'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'f'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'g'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'h'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'i'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'j'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'k'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'l'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'m'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'n'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'o'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'p'</span></span> <span class="p">}</span>
<span class="kd">local</span> <span class="n">remove</span> <span class="o">=</span> <span class="p">{</span> <span class="n">a</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span><span class="p">,</span> <span class="n">c</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span><span class="p">,</span> <span class="n">e</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span><span class="p">,</span> <span class="n">f</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span><span class="p">,</span> <span class="n">p</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span> <span class="p">}</span>

<span class="kd">local</span> <span class="k"><span class="function"><span class="keyword" style="font-weight:bold">function</span></span></span><span class="function"> <span class="nf"><span class="title" style="color:rgb(153,0,0); font-weight:bold">dump</span></span><span class="p"><span class="params">(</span></span><span class="params"><span class="n">table</span><span class="p">)</span></span><span class="p"></span>
<span class="k"><span class="title" style="color:rgb(153,0,0); font-weight:bold">for</span></span> <span class="n"><span class="title" style="color:rgb(153,0,0); font-weight:bold">k</span></span><span class="p">,</span> <span class="n"><span class="title" style="color:rgb(153,0,0); font-weight:bold">v</span></span> <span class="k"><span class="title" style="color:rgb(153,0,0); font-weight:bold">in</span></span> <span class="nb"><span class="title" style="color:rgb(153,0,0); font-weight:bold">pairs</span></span><span class="p"><span class="params">(</span></span><span class="params"> <span class="n">table</span> <span class="p">)</span></span><span class="p"></span> <span class="k"><span class="title" style="color:rgb(153,0,0); font-weight:bold">do</span></span>
<span class="nb"><span class="title" style="color:rgb(153,0,0); font-weight:bold">print</span></span><span class="p"><span class="params">(</span></span><span class="params"><span class="n">k</span><span class="p">)</span></span><span class="p"></span>
<span class="nb"><span class="title" style="color:rgb(153,0,0); font-weight:bold">print</span></span><span class="p"><span class="params">(</span></span><span class="params"><span class="n">v</span><span class="p">)</span></span><span class="p"></span>
<span class="nb"><span class="title" style="color:rgb(153,0,0); font-weight:bold">print</span></span><span class="p"><span class="params">(</span></span><span class="params"><span class="s2"><span class="string" style="color:rgb(221,17,68)">"*********"</span></span><span class="p">)</span></span><span class="p"></span>
<span class="k"><span class="title" style="color:rgb(153,0,0); font-weight:bold">end</span></span>
<span class="k"><span class="title" style="color:rgb(153,0,0); font-weight:bold">end</span></span></span><span class="k"></span>


说明:一般我们不在循环中删除,在循环中删除会造成一些错误。这是可以建立一个remove表用来标记将要删除的,如上面例子,把将要删除的标记为true
[title3]
方法1 从后往前删除[/title3]

<span class="k"><span class="keyword" style="font-weight:bold">for</span></span> <span class="n">i</span> <span class="o">=</span> <span class="o"><span class="comment" style="color:rgb(153,153,136); font-style:italic">#</span></span><span class="comment" style="color:rgb(153,153,136); font-style:italic"><span class="n">test</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="k">do</span></span><span class="k"></span>
<span class="k"><span class="keyword" style="font-weight:bold">if</span></span> <span class="n">remove</span><span class="p">[</span><span class="n">test</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span> <span class="k"><span class="keyword" style="font-weight:bold">then</span></span>
<span class="nb">table.remove</span><span class="p">(</span><span class="n">test</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>

<span class="n">dump</span><span class="p">(</span><span class="n">test</span><span class="p">)</span>


为什么不从前往后,朋友们可以测试,table.remove操作后,后面的元素会往前移位,这时候后续的删除索引对应的元素已经不是之前的索引对应的元素了。
[title3]
方法2 while删除[/title3]

<span class="kd">local</span> <span class="n">i</span> <span class="o">=</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">1</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">while</span></span> <span class="n">i</span> <span class="o"><=</span> <span class="o"><span class="comment" style="color:rgb(153,153,136); font-style:italic">#</span></span><span class="comment" style="color:rgb(153,153,136); font-style:italic"><span class="n">test</span> <span class="k">do</span></span><span class="k"></span>
<span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">if</span></span> <span class="n">remove</span><span class="p">[</span><span class="n">test</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span> <span class="k"><span class="keyword" style="font-weight:bold">then</span></span>
<span class="indent">  </span><span class="indent">  </span><span class="nb">table.remove</span><span class="p">(</span><span class="n">test</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
<span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">else</span></span>
<span class="indent">  </span><span class="indent">  </span><span class="n">i</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">1</span></span>
<span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>

<span class="n">dump</span><span class="p">(</span><span class="n">test</span><span class="p">)</span>

[title3]
方法3 quick中提供的removeItem[/title3]

<span class="k">function</span> <span class="nc">table</span><span class="p">.</span><span class="nf">removeItem</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">item</span><span class="p">,</span> <span class="n">removeAll</span><span class="p">)</span>
<span class="indent">  </span><span class="kd">local</span> <span class="n">rmCount</span> <span class="o">=</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">0</span></span>
<span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">for</span></span> <span class="n">i</span> <span class="o">=</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">1</span></span><span class="p">,</span> <span class="o"><span class="comment" style="color:rgb(153,153,136); font-style:italic">#</span></span><span class="comment" style="color:rgb(153,153,136); font-style:italic"><span class="n">list</span> <span class="k">do</span></span><span class="k"></span>
<span class="indent">  </span><span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">if</span></span> <span class="n">list</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="n">rmCount</span><span class="p">]</span> <span class="o">==</span> <span class="n">item</span> <span class="k"><span class="keyword" style="font-weight:bold">then</span></span>
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="nb">table.remove</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">i</span> <span class="o">-</span> <span class="n">rmCount</span><span class="p">)</span>
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">if</span></span> <span class="n">removeAll</span> <span class="k"><span class="keyword" style="font-weight:bold">then</span></span>
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="n">rmCount</span> <span class="o">=</span> <span class="n">rmCount</span> <span class="o">+</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">1</span></span>
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">else</span></span>
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">break</span></span>
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="indent">  </span><span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="indent">  </span><span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>

<span class="k"><span class="keyword" style="font-weight:bold">for</span></span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="k"><span class="keyword" style="font-weight:bold">in</span></span> <span class="nb">pairs</span><span class="p">(</span> <span class="n">remove</span> <span class="p">)</span> <span class="k"><span class="keyword" style="font-weight:bold">do</span></span>
<span class="indent">  </span><span class="n">table</span><span class="p">.</span><span class="n">removeItem</span><span class="p">(</span><span class="n">test</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>




Lua语言基础汇总(12)-- Lua中的面向对象编程

果冻想2014-08-11 14:08:322087 次阅读

简单说说Lua中的面向对象

Lua中的table就是一种对象,看以下一段简单的代码:

上述代码会输出tb1 ~= tb2。说明两个具有相同值得对象是两个不同的对象,同时在Lua中table是引用类型的。我在《Lua中的模块与包》中也总结了,我们是基于table来实现的模块,在table中可以定义函数,也就是说,每个table对象都可以拥有其自己的操作。看一段代码:

上面的代码创建了一个新函数,并将该函数存入Account对象的withDraw字段中,然后我们就可以调用该函数了。不过,在函数中使用全局名称Account是一个不好的编程习惯,因为这个函数只能针对特定对象工作,并且,这个特定对象还必须存储在特定的全局变量中。如果改变了对象的名称,withDraw就再也不能工作了。例如以下代码:

这样就会出现错误。我在这里使用Account创建了一个新的对象a,当将Account赋值为nil时,应该要对a对象不产生任何影响。但是,由于在函数withDraw内部使用了Account,而不是变量a,所以就出现了错误。如果我们将withDraw函数内部的Account.balance = Account.balance – v语句修改为:a.balance = a.balance – v,这样就不会出现错误了。这就表明,当我们需要对一个函数进行操作时,需要指定实际的操作对象,即这里的a,这就需要一个额外的参数来表示该操作者,就好比C++中的this一样,只不过这里将这个关键字换成了self,换完以后的代码如下:

这样再调用,就不会出现错误了。

使用self参数是所有面向对象语言的一个核心。大多数面向对象语言都对程序员隐藏了self参数,从而使得程序员不必显示地声明这个参数。Lua也可以,当我们在定义函数时,使用了冒号,则能隐藏该参数,那么上述代码使用冒号来改下,就是下面这个样子了。

冒号的作用很简单,就是在方法定义中添加一个额外的隐藏参数,以及在一个方法调用中添加一个额外的实参。冒号只是一种语法便利,并没有引入任何新的东西;如果你愿意,你可以可以不使用self,而是在每次定义一个函数时,手动的加上self,只要你处理好了self,它们都是一样的。

这里乱乱的讲了一些Lua中的东西,主要还是说了table是一个不一样的东西,还有self。接下来,就正式进入面向对象的世界。不要忘了,上面总结的东西是非常有用的。



类是什么?一个类就是一个创建对象的模具。例如C++中,每个对象都是某个特定类的实例。在C++中,如果一个类没有进行实例化,那这个类中对应的操作,基本就是一堆“没有用”的代码;而Lua则不一样,即使你不实例化一个“类”,你照样也可以使用“类”名直接调用它的方法(对于C++,请忽视静态的方法);这说明Lua中的“类”的概念与C++这种高级语言中类的概念还是有差别的。在Lua中则没有类的概念,而我们都是通过Lua现有的支持,去模拟类的概念。在Lua中,要表示一个类,只需创建一个专用作其他对象的原型(prototype)。原型也是一种常规的对象,也就是说我们可以直接通过原型去调用对应的方法。当其它对象(类的实例)遇到一个未知操作时,原型会先查找它。

在Lua中实现原型是非常简单的,比如有两个对象a和b,要让b作为a的原型,只需要以下代码就可以完成:

设置了这段代码以后,a就会在b中查找所有它没有的操作。若将b称为是对象a的“类”,就仅仅是术语上的变化。现在我就从最简单的开始,要创建一个实例对象,必须要有一个原型,就是所谓的“类”,看以下代码:

好了,现在有了原型,那如何使用这个原型创建一个“实例”呢?接着看以下代码:

当调用Account:new时,self就相当于Account。接着,我们就可以调用Account:new来创建一个实例了。再看:

上面这段代码是如何工作的呢?首先使用Account:new创建了一个新的实例对象,并将Account作为新的实例对象a的元表。再当我们调用a:display函数时,就相当于a.display(a),冒号就只是一个“语法糖”,只是一种方便的写法。我们创建了一个实例对象a,当调用display时,就会查找a中是否有display字段,没有的话,就去搜索它的元表,所以,最终的调用情况如下:

a的元表是Account,Account的__index也是Account。因此,上面的调用也可以使这样的:

所以,其实我们可以看到的是,实例对象a表中并没有display方法,而是继承自Account方法的,但是传入display方法中的self确是a。这样就可以让Account(这个“类”)定义操作。除了方法,a还能从Account继承所有的字段。

继承不仅可以用于方法,还可以作用于字段。因此,一个类不仅可以提供方法,还可以为实例中的字段提供默认值。看以下代码:

在Account表中有一个value字段,默认值为0;当我创建了实例对象a时,并没有提供value字段,在display函数中,由于a中没有value字段,就会查找元表Account,最终得到了Account中value的值,等号右边的self.value的值就来源自Account中的value。调用a:display()时,其实就调用以下代码:

在display的定义中,就会变成这样子:

第一次调用display时,等号左侧的self.value就是a.value,就相当于在a中添加了一个新的字段value;当第二次调用display函数时,由于a中已经有了value字段,所以就不会去Account中寻找value字段了。

继承

由于类也是对象(准确地说是一个原型),它们也可以从其它类(原型)获得(继承)方法。这种行为就是继承,可以很容易的在Lua中实现。现在我们有一个类(原型,其实在Lua中说类这个概念,还是很别扭的,毕竟用C++的脑袋去想,还是觉的有点奇怪的。)CA:

现在需要从这个CA类派生出一个子类CLittleA,则需要创建一个空的类,从基类继承所有的操作:

现在,我创建了一个CA类的一个实例对象,在Lua中,现在CLittleA既是CA类的一个实例对象,也是一个原型,就是所谓的类,就相当于CLittleA类继承自CA类。再如下面的代码:

CLittleA从CA继承了new;不过,在执行CLittleA:new时,它的self参数表示为CLittleA,所以s的元表为CLittleA,CLittleA中字段__index的值也是CLittleA。然后,我们就会看到,s继承自CLittleA,而CLittleA又继承自CA。当执行s:display时,Lua在s中找不到display字段,就会查找CLittleA;如果仍然找不到display字段,就查找CA,最终会在CA中找到display字段。可以这样想一下,如果在CLittleA中存在了display字段,那么就不会去CA中再找了。所以,我们就可以在CLittleA中重定义display字段,从而实现特殊版本的display函数。

多重继承

说到多重继承,我在写C++代码的时候也用的很少,一般都是使用组合的方式解决的,对于“组合”这个概念不明白的朋友,可以阅读我的设计模式系列的文章。既然说到了Lua中的多重继承,那也总结一下,顺便开拓一下视野和知识面。

实现单继承时,依靠的是为子类设置metatable,设置其metatable为父类,并将父类的__index设置为其本身的技术实现的。而多继承也是一样的道理,在单继承中,如果子类中没有对应的字段,则只需要在一个父类中寻找这个不存在的字段;而在多重继承中,如果子类没有对应的字段,则需要在多个父类中寻找这个不存在的字段。



就像上图表示一样,Lua会在多个父类中逐个的搜索display字段。这样,我们就不能像单继承那样,直接指定__index为某个父类,而是应该指定__index为一个函数,在这个函数中指定搜索不存在的字段的规则。这样便可实现多重继承。这里就出现了两个需要去解决的问题:

1. 保存所有的父类;

2. 指定一个搜索函数来完成搜索任务。

对于以上的多重继承,我们来看一段头疼的代码:

代码虽然头疼,但是还的继续看。首先大体阅读一下上面的代码,看不懂不要紧。现在我来解释上面的代码。

使用createClass创建了一个类(原型),将CA和CB设置为这个类(原型)的父类(原型);在创建的这个类(原型)中,设置了该类的__index为一个search函数,在这个search函数中寻找在创建的类中没有的字段;

创建的新类中,有一个构造函数new;这个new和之前的单继承中的new区别不大,很好理解;

调用new构造函数,创建一个实例对象,该实例对象有一个name字段;

调用object:setName(“JellyThink”)语句,设置一个新的名字;但是在objectC中没有这个字段,怎么办?好了,去父类找,先去CA找,一下子就找到了,然后就调用了这个setName,setName中的self指向的是objectC;设置以后,就相当于修改了objectC字段的name值;

调用objectC:getName(),objectC还是没有这个字段。找吧,CA也没有,那就接着找,在CB中找到了,就调用getName,在getName中的self指向的是objectC。所以,在objectC:getName中返回了objectC中name的值,就是“JellyThink”。

还有什么?什么也没有了,对于多重继承,貌似看起来很难,很麻烦,其实也就这么点东西。不懂的话,再来一遍。

我拿什么保护你

我们都知道,在C++或Java中,对于类中的成员函数或变量都有访问权限的。public,protected和private这几个关键字还认识吧。那么在Lua中呢?Lua中是本身就是一门“简单”的脚本语言,本身就不是为了大型项目而生的,所以,它的语言特性中,本身就没有带有这些东西,那如果非要用这样的保护的东西,该怎么办?我们还是“曲线救国”。思想就是通过两个table来表示一个对象。一个table用来保存对象的私有数据;另一个用于对象的操作。对象的实际操作时通过第二个table来实现的。为了避免为授权的访问,保存对象的私有数据的表不保存在其它的table中,而只是保存在方法的closure中。看一段代码:

这种设计给予存储在self table中所有东西完全的私密性。当调用newObject返回以后,就无法直接访问这个table了。只能通过newObject中创建的函数来访问这个self table;也就相当于self table中保存的都是私有的,外部是无法直接访问的。大家可能也注意到了,我在访问函数时,并没有使用冒号,这个主要是因为,我可以直接访问的self table中的字段,所以是不需要多余的self字段的,也就不用冒号了。

总结

这篇文章对Lua中的“面向对象”进行了一些简单的总结,本来Lua就很简单,我们只是使用了Lua本身的特性去实现一些更高大上的特性,这样没有什么不好,有的时候也没有什么好。要不要用面向对象的这些特性,在具体项目中具体分析,至于如何理解面向对象的概念,不是这里的重点。Lua的面向对象,总结到此为止,以后有了实际的项目应用,接着总结,这篇基础篇的文章,希望对大家有用。

**************************************************


理解lua 语言中的点、冒号与self lua 中 require

2013年07月29日 ⁄ 综合 ⁄ 共 603字 ⁄ 字号 小 中 大 ⁄ 评论关闭

lua编程中,经常遇到函数的定义和调用,有时候用点号调用,有时候用冒号调用,这里简单的说明一下原理。

girl = {money = 200}
function girl.goToMarket(girl ,someMoney)
girl.money = girl.money - someMoney
end
girl.goToMarket(girl ,100)
print(girl.money)


可以看出,这里进行了方法的点号定义和点号调用。

boy = {money = 200}
function boy:goToMarket(someMoney)
self.money = self.money - someMoney
end
boy:goToMarket(100)
print(boy.money)


这里进行了冒号定义和冒号调用。

以上的打印结果都是100。

可以看出,冒号定义和冒号调用其实跟上面的效果一样,只是把第一个隐藏参数省略了,而该参数self指向调用者自身

当然了,我们也可以点号定义冒号调用,或者冒号定义点号调用

如:

boy = {money = 200}
function boy.goToMarket(self ,someMoney)
self.money = self.money - someMoney
end
boy:goToMarket(100)
print(boy.money)


总结:冒号只是起了省略第一个参数self的作用,该self指向调用者本身,并没有其他特殊的地方
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: