Lua的语法实际上和c的语法很接近,看起来也比较的简洁.(Lua5.1版本)
Lua的关键字如下:
and break do else elseif
end false for function if
in local nil not or
repeat return then true until while
Lua的基本符号如下:
+ - * / % ^ # == ~= <= >= < > = ( ) { } [ ] ; : , . .. ...
其中几个与C不同的符号有: ^符号表示幂运算,例如 12^2,表示12的平方.
#符号针对的是table(表)类型,用于得到table的长度,例如 a = {123,456};print(#a);那么结果会输出2.
~=符号表示 不等于,相当于C语言里的!=.
..符号表示字符串的链接,例如print("d".."e");那么结果会输出de.
...符号则和C语言一样,用于函数的形参,来表示可变参数.
.和:符号其实是一个作用,和C语言中的结构体的使用差不多,例如io.read()和io:read()是一样的,都是调用io下的read()函数.
Lua的语法:
1.注释和一些说明
lua中的注释使用的是--来进行单行注释,和C语言中的//一样只是符号换了个而已.
lua中的多行注释使用的是--[[ 和 ]],分别和C语言中的/*和*/对应.
另外lua中的语句的分隔符可以是空格或者分号,也就是说
a = 123 print(a)语句和a = 123;print(a);两种写法都可以,但还是建议用分号来分隔语句,这样更明确(也更像C了,后面我们使用的所有分号都是基于这个原因).
2.变量
lua中的变量没有类型,也就是说一个变量可以是任何类型.例如:
a = 123;-->数值类型
a = "str";-->字符串类型
a = [[abc
def
]]
-->这是字符串的第二种写法,[[和]]中间的内容被视为字符串,且不会解释转义序列,同时如果第一个字符是换行符则无视这个字符.
a = {"sad",152};-->table(表)类型(表中的数据可以是各种类型),对于任何一个变量,随时可以赋予任意的类型值.
a = function()
print("function a");
end
-->同样,a也可以是函数类型(函数的具体语法后面会介绍),上面这种写法和下面是一样的:
function a()
print("function a");
end
-->这也引出了lua的一大特色,一切都是变量!
3.变量类型
有nil, boolean, number, string, function, userdata, thread, table八种类型.
nil:本意是0的意思,但是lua将这个作为单独的类型,用于表示不存在.
nil这个类型需要详细解释,nil作为一种类型,这个类型下的值也只有一个,那就是nil,也就是说,
如果a = nil;那么就意味着lua中不再存在这个变量了(如果原来这个变量里有值,那么这个变量就会被lua删除);
同样,访问一个lua中不存在的变量,也会返回nil,例如print(novar);如果前面没有定义novar,那么输出的结果就是nil.
boolean:有两个值,true和false,用于表示真和假,但是要注意,在判断中,nil也被认为是假,但是false却不能和nil等价.
在判断中只有false和nil被认为是假!数字0也被认为是true的.
string:字符串类型
function:函数类型
table:表类型,一个表里可以有各种类型的数据,例如a = {123,"sad",function() print(156) end},同样table有自己的索引
a中的数据可以通过索引访问:例如a[1],a[2],a[3],(注意,lua中下标从1开始).当然也可以自己定义下标:a = {add = 123,sou = "sad"};
这样就可以通过a.add或者a["add"]来访问其中add表示的数据了.当然,如果下标指向了不存在的地方,那么就会返回nil了.对于table还有
一点需要注意,a = {[1] = 123,456}这个时候其实a中只有一个值,那就是a[1],值为456.也就是说,在一个表中,lua会按照从前到后的顺序
对你没有写过下标的数据进行标号,下标从1开始,也就是说,如果你的表中定义了[1] = 123,那么实际上会被lua用456覆盖掉,lua不会检测
这个下标是否存在.还有一点要说明:在table中,add = 123和["add"] = 123是等价的.
userdata:用户数据类型(后面再说)
thread:线程类型(后面再说)
虽然有各种类型,但是lua中的变量都是由lua来进行类型管理的.
4.逻辑运算符
and运算符:
c = a and b;当a为true的时候,c = b,当a为false的时候c = a;
or运算符:
c = a or b,当a为true的时候,c = a,当a为false或nil的时候 c = b;
对于这两个运算符,实际上是逻辑的一种变形,and是逻辑与的变形,or是逻辑或的变形.说是变形是因为他们返回的不是true和false,
而是参与运算的变量的值.可以这么认为,对于and(逻辑与),首先判断左操作数的的真假,如果为真,那么左操作数就不能决定这个逻辑与
的真假,而由右操作数决定,因此返回了右操作数.如果左操作数为假,那么这个逻辑与的结果就是假,也就是说,左操作数决定了这个表达式
的真假,所以返回左操作数.也就是说,按照从左到右的顺序判断操作数的真假,如果该操作数能决定整个逻辑的真假,那就返回这个操作数.
举个例子:
a = 1;b = 2;c = nil;e = a and b and c;对于这个式子,首先从左到右,先看a && b,a为真,所以这个这个表达式的真假决定于b,因此
返回b,于是变成了 e = b and c;同时b && c中,b不能决定表达式的真假,因此由c来决定.所以最后返回c,所以e的值为nil;
or的逻辑或也可以这样.a = 1;b = 2;c = nil;e = a or b or c;从左到右,a可以决定a || b的真假(此时为真),所以返回a,同理a||c的结果
也就是a了,所以最后e 的值为1;
小技巧:a = a or 12;这个用法是当a为假或者nil的时候给a赋值为12.
a and b or c;这个表达式模拟了c语言中的 a?b:c;的语法.
5.赋值和逻辑控制
赋值:a = 8;-->一般赋值
b,c,d = 45,a,"sdee";-->lua中支持这么赋值,
a,b = 1;-->当然,如果左边的变量数大于右边的参数数量,就会用nil补齐.所以a = 1;b = nil;
a = 1,2;-->当右边的参数数量大于左边的变量数量,那么多余的参数就会被舍弃,所以a = 1;
a,b=b,a;-->交换a和b的值
if语句:
if 条件1 then
代码1
elseif 条件2 then
代码2
else
代码3
end;
-->基本if的结构就是这样了,当然实际使用的时候可以没有elseif和else,但是end不可以少.
循环语句:
while 条件 do
代码
end;
repeat
代码
until 条件;
上面两种循环语句和C语言中的while和do...while是一样的.
for语句:
一般的for:
for i = 1,f(x),2 do
代码
end;
-->这是一种十分一般的用法,i=1表示i是控制变量,1是初始值,f(x)实际上是调用f()函数,并把函数的返回值作为循环的终止值
(也就是当i变量到这个值的时候循环就终止了,函数只在开始循环的时候调用一次),当然终止值也可以直接使用一个数字.最后的2
表示每次循环i的增量(当然-2也是可以的,如果这个增量省略不写的话,那么默认是1);
泛型for:
a = {x = 66,"dsad",554,[5] = 444};
for i,v in ipairs(a) do
print(i,v);-->print()函数可以接受任意的参数数量,然后在一行中输出,中间用制表符分隔;
end;
->ipairs()函数会返回a表中的数字下标和对应的值.这个需要特别说明:这个函数实际上是一个闭包(这个概念会在后面说明),暂时
不用管这个概念,我们只要知道i会是a表中的下标,v是a表中对应下标的值就行.注意,这个下标的限制,这个下标只从1开始,到某
个下标对应的值为nil的时候终止.也就是说,实际上输出的是:
1 dsad
2 554
只会输出两行,也就是说x是不会被输出的,同时下标5的也不会输出,因为到下标为3的时候值已经是nil了(即不存在).
for i,v in pairs(a) do
print(i,v);
end;
但是如果我们把ipairs()函数改成pairs()函数就不一样了.
输出如下:
1 dsad
2 554
5 546
x 66
所有的下标和值都会被输出来(注意一下顺序,也就是说数字下标会先输出,而非数字下标后输出,即使x定义在最前面).
块的概念:
do
代码
end
do...end中间就是一个区块,本身并没有太大的作用,但是在某些情况下,例如,如果你想定义一个a,这个a只在某个代码块中被用到,但是你
不想在其他地方被访问到,那么你就可以把你的a和循环都写在这个do...end块中.例如:
do
local a = 0;
--你的代码
end;
这样的话 a就只能在这个do...end中被访问到了.当然a的定义前面要加上local来表示是局部变量(这个局部区域就是这个块).
当然,没有声明为local的变量就是全局变量了.
6.函数和闭包
函数的基本写法前面介绍变量的时候已经说过了.只不过我们通常采用下面这种方式来声明函数:
function func(a,b)
print(type(a),type(b));-->type()函数用于输出参数的类型
return a,b;
end;
基本的函数就是这样,有参数,有返回值.但是要注意一点,如果你调用这个函数的时候,fun(123,"de",555);如果你给出的参数比函数的参数
要多的话,那么多出的参数会被无视掉.当然如果你给出的参数少了,就会用nil来填补.另外注意一点,lua的函数是可以返回多个值的,只要中间
用逗号分隔就行.对于返回值也一样,a,b,c = func(...),这个函数返回了两个数据,但是用于接受的有三个变量,此时c会用nil来填充.用于接受
的变量如果比返回值少,那么返回值的顺序给变量赋值,多出的返回值被丢弃.注意:函数可以没有返回值,可以不写return.
lua的函数也支持可变参数:
function func(a,b,...)
for i,v in ipairs(arg) do
print(i,v);
end;
end;
-->...表示可变参数,但是实际上,这个可变参数会被视为一个arg表,因此我们可以用循环的方式来读取每个arg表中的数据,上面的代码
中i就是参数的编号,从1开始,v就是数据.但是注意,a和b不属于arg表.
闭包:
先来看看嵌套的函数:
function func(a,b)
function u()
print(a,b);
end
u()
end;
首先我们在func()函数内定义了u()函数,然后调用u()函数,仔细看,u函数可以调用func的变量!
于是闭包就是:
function func(a,b)
c = 12;
return function()
print(a,b,c);
end;
end;
将函数返回!被返回的函数可以访问func的a,b,c变量,如果调用很多次func函数,都有自己的a,b,c变量,他们之间不会互相干扰,就像
之前说到的他们是块中的局部变量,这个变量是可以改变的.
闭包与泛型for:
for i,v in ipairs(a) do
代码
end;
此处的ipairs()函数实际上就是返回一个闭包函数,而且for循环在每次循环之前都会调用这个闭包函数,以取得i和v的值.返回的闭包函数的原理基本是这个样子:使用之前说到的局部变量来保存遍历到的下标,然后判断下标对应的值是不是nil,如果是的话就返回nil以中止循环,否则就返回下标和对应的值.(这里的for循环实际上是判断返回值是不是nil来确定是否停止循环,是nil则停止循环).
7.协程(纤程,Fiber)
以这个函数为例:
function func(a,b)
print(a,b);
c,d = coroutine.yield(a,b);
print(c,d);
return c;
end;
首先说明一下协程的建立:
co = coroutine.create(func);-->这里的co的类型实际上就是前面提过的thread类型!
当这么做以后,func()并不会执行,而是出于挂起的状态,然后调用:
m,n = coroutine.resume(co,1,123);-->注意,这里的1和123就会作为函数的参数!
则可以使函数开始执行,当函数执行到c,d = coroutine.yield(a,b); 这一句的时候就会暂停,然后将a,b返回,然后继续挂起.
这时候,m和n的值实际就是函数里传出来的a和b的值了.然后我们继续调用:
coroutine.resume(co);-->这里我们不传参数
然后就会从刚才暂停的地方继续执行!然后将传入的值赋值给c和d,但是由于我们没有传值,所以两个变量都是nil.然后直到函数执行
结束,因为我们在函数里只放了一个yield()函数(当然可以放很多个的).执行结束之后,函数就处于终止状态了.
上面我们所说的这个co实际上就是协程,而这个协程的函数就是func.
如果在某个时刻,我们想知道协程的状态,可以调用coroutine.status(co);以获得状态.