Lua极简入门指南(一):基础知识篇
本文是《ProgramminginLua3rd》读书笔记。
Chunks
一个Chunk就是一组被执行的语句,例如一个文件或者交互模式下的一行。
标识符(identifiers)
我们应该避免使用以_开头并跟上一个或者多个大写字母的字符串来作标识符,它们被保留作特殊的用途(例如:_VERSION)。
注释
单行注释使用
--
多行注释使用
--[[和--]]
类型简介
Lua存在的数据类型包括:
1.nil。此类型只有一个值nil。用于表示“空”值。全局变量默认为nil,删除一个已经赋值的全局变量只需要将其赋值为nil(对比JavaScript,赋值null并不能完全删除对象的属性,属性还存在,值为null)
2.boolean。此类型有两个值true和false。在Lua中,false和nil都表示条件假,其他值都表示条件真(区别于C/C++等语言的是,0是真)
3.number。双精浮点数(IEEE754标准),Lua没有整数类型
4.string。你可以保存任意的二进制数据到字符串中(包括0)。字符串中的字符是不可以改变的(需要改变时,你只能创建一个新的字符串)。获取字符串的长度,可以使用#操作符(长度操作符)。例如:print(#”hello”)。字符串可以使用单引号,也可以使用双引号包裹,对于多行的字符串还可以使用[[和]]包裹。字符串中可以使用转义字符,例如\n\r等。使用[[和]]包裹的字符串中的转义字符不会被转义
5.userdata。用于保存任意的C数据。userdata只能支持赋值操作和比较测试
6.function。函数是第一类值(first-classvalue),我们能够像使用其他变量一样的使用函数(函数能够保存在变量中,可以作为参数传递给函数)
7.thread。区别于我们常常说的系统级线程
8.table。被实现为关联数组(associativearrays),可以通过任何值来进行索引(nil除外)。和全局变量一样,table中未赋值的域为nil,删除一个域只需要将其赋值为nil(实际上,全局变量就是被放置在一个table中)
type函数用于返回值的类型:
print(type("HelloWorld"))-->string print(type(10.4*3)) -->number print(type(print)) -->function print(type(type(X))) -->string
在Lua中,任何的变量都可以保存任何的值。
table使用简介
使用构造表达式可以创建一个table:
--创建一个空的table a={} --创建并初始化一个table,这里 --days[1]=="Sunday" --days[2]=="Monday" --... days={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"} --创建并初始化一个table,这里 --a["x"]==10 --a["y"]==20 a={x=10,y=20}
使用[]操作符访问table的域:
a={} k="x" a[k]=10 a["x"]=20 print(a["y"])-->nil a.x=30
注意,a.name的语法等价于a["name"]。
table可以用于表示数组,这时候索引为整数,并且从1(而非0)开始,例如:
a={'a','b'} a[1]=='a' a[2]=='b'
长度操作符可以获取table数组部分的长度:
a={} a[1]=1 a[2]=2 print(#a)-->2 a.a=1 a.b=2 print(#a)-->2 a={} a.a=1 a.b=2 print(#a)-->0
表达式
算术操作符
1.+(加)
2.-(减)
3.*(乘)
4./(除)
5.^(幂)
6.%(取模)
任何算术操作符都试图将操作数转换为数值类型,例如:
print(10+'1')-->11
关系操作符
1.<(小于)
2.>(大于)
3.<=(小于等于)
4.>=(大于等于)
5.==(等于)
6.~=(不等于)
两个不同类型的值是不相等的,例如:
nil~=false
table、userdata类型是通过引用进行比较的,例如:
a={};a.x=1;a.y=0 b={};b.x=1;b.y=0 c=a
这里a和c引用一个相同的对象,因此a==c,但是a~=b(即便a、b内容相同)。
逻辑操作符
1.and
2.or
3.not
逻辑操作符有返回值。对于and操作来说,如果第一个操作数为false时返回此操作数,否则返回第二个操作数。对于or操作来说,如果第一个操作数不为false时返回此操作数,否则返回第二个操作数。
连接操作符
字符串连接可以使用连接操作符“..”,例如:
print("Hello".."World")
连接操作符试图将操作数转化为字符串,例如:
print("number:"..1)
语句
多赋值(multipleassignment)支持,例如:
a,b=1,2 print(a)-->1 print(b)-->2
多赋值的一个惯用法就是交换两个变量的值:
x,y=1,2 x,y=y,x print(x)-->2 print(y)-->1
创建局部变量使用local:
j=10 --全局变量j locali=10--局部变量i
局部变量的作用域限制于他们声明的块(block)。块(block)包括:
1.控制结构的主体部分
2.函数体
3.chunk
4.do-end
范例:
iftruethen localx=20 print(x)-->20 end print(x)-->nil
我们可以使用do-end关键字来构造一个块:
do localx=20 print(x)-->20 end print(x)-->nil
访问局部变量要快于访问全局变量。在Lua中有一个习惯用法:
localfoo=foo
用于创建一个局部变量并初始化为同名的全局变量。这样做常常出于两个原因:
1.避免某些类型的全局变量被修改
2.提高访问速度
控制结构
ifthenelseifelse
ifa<0then a=0 end ifa<bthen returna else returnb end ifop=='+'then r=a+b elseifop=='-'then r=a-b elseifop=='*'then r=a*b elseifop=='/'then r=a/b else error('invalidoperation') end
Lua中没有switch语句。
while
locali=1 whilea[i]do print(a[i]) i=i+1 end
repeat
repeat line=io.read() untilline~='' print(line)
区别于while,repeat会先执行循环体,然后判断测试条件。
数值型for(numericfor)
for有两种:
数值型for(numericfor)
1.泛型for(genericfor)
2.数值型for的语法如下:
forvar=exp1,exp2,exp3do <something> end
这里exp1作为var的初始值,exp2为var的最大值,exp3为var每次递增的值,exp3是可选的,默认为1。范例:
--输出123 fori=1,3do print(i) end
有一些需要注意的地方:
1.for中的exp1、exp2、exp3只会被计算一次值,例如:
fori=1,f(x)doprint(i)end
这里的f(x)只会被调用一次
2.控制变量var只是一个局部变量
3.不要尝试去修改控制变量var的值(结果是未知的)
泛型for
泛型for通过一个迭代器函数来实现遍历,例如:
fork,vinpairs(t)do print(k,v) end
这里的pairs就是一个迭代器函数,此for循环遍历tablet,每次获取到的key保存在变量k中,获取到的value保存在变量v中。除了pairs还有其他的迭代器可以用:
1.io.lines可用于迭代文件中的行
2.ipairs可用于迭代table的数组部分
我们还可以自己编写迭代器。
break、return、goto
break语句用于跳出一个循环(for、repeat、while)。
return语句用于为函数返回结果。在Lua中,return语句必须是一个块的最后一条语句,看一个例子:
functionfoo() --语法错误 return locali=1 end
有时候,我们出于某些原因(例如为了debug),我们需要在一个函数中插入一个return语句,这时候可以这么做:
functionfoo() --... doreturnend --... end
goto语句用于在函数中跳转。goto语句可以让执行跳转到特定的标签(label)处,例如:
gotoquit print('comeon') ::quit:: print('quit')
这里输出quit。正如我们看到的,标签的写法为::name::。goto跳转也是存在限制的:
1.不允许跳转到一个块中去
2.不允许跳转到函数之外去
3.不允许跳入局部变量的作用域中
对于第三点,看一个例子:
gotoquit locala ::quit:: print('quit')
这里,会出现语法错误(jumpsintothescopeoflocal'a')。但是,有一个细节需要注意,我们先修改上面的例子:
gotoquit locala ::quit::
执行成功,没有语法错误。这是因为局部变量的作用域结束于变量定义的块的最后一个非void语句,而标签被认为是一个void语句,对于上面的例子来说,a的作用域在::quit::之前就结束了,因此gotoquit并没有跳入局部变量a的作用域中。
利用goto可以比较方便的编写状态机,例如(s1、s2为状态):
::s1::do localc=io.read(1) ifc=='0'thengotos2 elseifc==nilthenprint'ok';return elsegotos1 end end ::s2::do localc=io.read(1) ifc=='0'thengotos1 elseifc==nilthenprint'notok';return elsegotos2 end end gotos1