一、操作符简介
如同C语言,Lua的操作符也是常见的那几个,比如,算数类的有:加减乘除,求模,指数,负号。关系类的有:大(等)于,小(等)于,等于和不等于。逻辑类的有:与或非。
算数类的都是常识,不赘述,我们先来看关系类。
Lua的关系操作运算结果为true或false,其中,不等于用“~=”表示,这里应该注意的是:对于相等性比较的两边,应具有相同的类型,否则直接返回true。如果参与比较的两边类型相等,那么则根据两边的类型来进一步比较,例如,数值型按照数字大小对比,而table则是引用比较,即不比较内容,比较引用的对象,举个书上的例子:
a = {}
b = {}
a.x = 1; a.y = 0
b.x = 1; b.y = 0
c = a
--该例子的对比结果为:a等于c,a不等于b,b不等于c
对于大小比较,最好是是数值和数值,字符串和字符串这样的比较。Lua会按照字母顺序依次对字符串做比较,而数值和字符串对比则会引发错误。其他类型不能作为大小比对。
关于逻辑类,与或非,对应and,or,not。与和或操作,即a and b,a or b,在a和b都不等于nil的情况下,返回ture或false。有nil的情况下可能返回nil,nil和false称为“假”,其他统称为“真”。以下为测试用例:
a = 1
b = 6
c = false
print(a and b)
print(b and a)
print(a and c)
print(b and true)
print(a and nil)
print(c and a)
print(a or b)
print(b or a)
print(b or c)
print(c or b)
print(true or a)
print(c or nil)
print(nil or c)
--[[ 返回值: 6 1 false true nil false 1 6 6 6 true nil false --]]
可以看出:对于and操作,左边为真,则返回右边的值,左边为假,则跳过右边,返回左边的值;对于or操作,左边为真,则跳过右边,返回左边的值,左边为假,则返回右边的值。
而not操作则是取反,返回ture或false,需要注意的是,0是实数,在Lua中代表真,空字符串也是真,not 0的结果和not ” “的结果都是false,这点很容易搞混,我曾经在项目中碰到这样的bug,百思不得其解,最后主程告诉我”not 0 == false”,初学者判断时容易搞混,要记住!测试几个not用法:
a = 1
b = "x"
print(not a)
print(not false)
print(not nil)
print(not true)
print(not b)
print(not 0)
--[[ 返回值: false true true false false false --]]
来看几个教程列出的技巧:
x = x or v
这段代码可以在没有设置x的时候给予其一个默认值v。分析:x or v,若x有赋值,且不为nil和false,则返回x,若x无赋值(=nil)或赋予false,则返回v。
(a and b) or c –b不为假
这段代码等价于C语言的a ? b : c,分析:在b为真的前提下,若a为假,a and b 为假且返回a,a or c返回c,因此表达式返回c;若a为真,a and b 为真且返回b,b or c返回b,则表达式返回b。符合a ? b : c的规则。
以上是Lua中的操作符基础,下面来看它们的优先级。(由高至低)
^(指数运算)
not,#(长度操作符),-(负号)
*,/,%
+,-(减号)
..(字符串连接)
<,>,<=,>=,~=,==
and
or
二、语句介绍
Lua和C差不多,赋值操作将等号右边的值给左边,也有if和while的控制语句,但应注意语法:
if控制结构:if 条件 then 结果 end,当有else时,if 条件 then 结果1 else 结果2 end,当有多重嵌套,表现为:
--伪代码
if 条件1 then
结果1
elseif 条件2 then
结果2
else 条件3 then
结果3
end
这里要注意几点:条件之后有个then不要忘了,整个控制结构结束时有个end别漏了。受C的影响,一开始是很容易忽略的,没有加上则会产生syntax error,久了习惯就好。嵌套时else和if是不分开的,如果写成else if是错的。
while控制结构:while 条件 do 循环体 end
和C一样,先检测条件,若为真,进入循环体一次,若为假,退出。这里要注意的是条件之后有个do不要忽略,最后结束有个end不要遗漏。
repeat控制结构:repeat 循环体 until 条件
即C语言的do-while结构,先执行循环体,再判定条件,若为真,继续循环,若为假,退出。这里要注意的是这个循环体至少都会执行一次,整个结构没有end,不要随意添加。若在循环体中有local声明的局部变量,在until的条件测试中也可以使用这个变量。
for循环(数字型):for var=exp1,exp2,exp3 do 循环体 end
这个的含义是变量var从exp1到exp2变化,每次变化的步长是exp3。其中exp3可以不指定,则默认为1。exp2可以设置为math.huge,则为不设上限。要注意的是这里for的条件内容不需要括号抱起来,跟在条件后面有个do,结尾有个end,都不要遗漏了。而for后面的三个表达式是在循环开始前一次求值,只执行一次,并且控制变量会自动声明为for的局部变量,循环结束后则不存在。
break和return:break是跳出包含它的内部循环,在多重循环中,会跳出内部后在外部一层继续执行。return是从函数返回结果,或结束一个函数执行。
以上是和C语言类似的几个语句,下面来看Lua的特殊语句:
首先,Lua支持多重赋值,并且会将等号右边的数量调整到和左边的个数一致,来看例子:
a,b,c,d,e = 1,2,3,4,5,6,7
print(a,b,c,d,e)
i,j,k,l,m,n = 1,2,3
print(i,j,k,l,m,n)
x=10
y=26
print(x,y)
x,y = y,x
print(x,y)
--[[返回值: 1 2 3 4 5 1 2 3 nil nil nil 10 26 26 10 --]]
可以看出,多重赋值情况下,若右边个数大于左边,则按顺序赋值后,抛弃多余的。若右边个数小于左边,则左边没赋值的置nil。第三个例子是交换操作,则不需要像C语言那样用temp中间数来交换两个数,因为多重赋值中,Lua会先对等号右边的元素求值,然后再赋值。
变量类型:Lua有全局变量和局部变量。
声明一个变量,只需赋值即可,不需要诸如int,float,char之类的类型,Lua会根据你的赋值,自动判断这个变量的类型;若不赋值,则默认赋予nil。因此,Lua中只有全局和局部变量,局部变量是在变量前面加local,比如local x=0,这个x就是局部变量,若该声明在一个循环中,则循环退出后,变量就不可使用了。即局部变量的作用域是声明它的那个块。除了循环体,函数体,Lua还有程序块的概念,即可以使用以下语句来界定块:
do 程序体 end
在交互模式中用这种模式很方便,每当遇到do,解释器就不会单独执行一行一行的语句,直到遇到end,这样就可以很好地将local局部变量包括在内使用。而尽可能多使用局部变量也是Lua的作者所倡导的,毕竟这样做效率高,也避免污染全局环境,方便垃圾收集器回收无用变量。
最后我们来看下泛型for循环(与前面说的数字型for是不同的):
for i, v in ipairs(数组table) do 执行体 end
for k,v in pairs(通用table) do 执行体 end
泛型for是铜鼓迭代器函数来遍历table的所有值。这两个就是泛型for的语法,区别在于pairs是迭代table元素,key可以不是数值,每次循环,k赋值为索引,v赋值为索引对应值;而ipairs是迭代数组元素的,key是数值,每次循环,i赋值为索引值,v赋值为对应索引的数组元素值。泛型for看似简单,在Lua中的功能却非常强大,使用也很广泛,例如在做游戏项目的时候,寻路、遍历地图、碰撞检测等都可能要用到它。我们来看个简单的例子:
tbl={a = 6, b = 9, yes = 1, no = 0}
for k,v in pairs(tbl) do
print(k.." ".."=".." "..v)
end
--[[返回值: a = 6 b = 9 yes = 1 no = 0 --]]
test={[1] = "yes", [2] = "Tuesday", [3] = "March"}
for i,v in ipairs(test) do
print(i.." ".."=".." "..v)
end
--[[返回值: 1 = yes 2 = Tuesday 3 = March --]]
关于泛型for,后续在迭代器的介绍中还会有更详细的说明。本次学习笔记到此结束,下次将进入函数的学习。