表达式
最后更新于:2022-04-01 20:44:04
在Lua中,表达式包括:数值常量、字符串字面值、变量、单目和双目运算符,函数调用,也包括一些非传统的函数定义和表结构。
## 1.算术运算符
Lua支持常规的算术运算符:'+', '-', '*', '/', '^', '%', '-'(负)。所有这些运算符都对实数起作用。举个例子,x^0.5, 计算x的平方根, x^(-1/3),计算x的立方根的反数。
在Lua中,'%'运算符定义规则如下:
a%b == a - floor(a/b)*b
对整型参数来说,它表示常规的意义,结果跟第二个参数b符号相同。
对实数来说,它有一些额外的用途。例如,x%1 表示x的小数部分,x-x%1 表示x的整数部分。类似的,x-x%0.01 将x精确到小数点后2位。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-09-06_57ce5eef244ae.PNG)
## 2.关系操作符
Lua提供了如下关系操作符:
< > <= >= == ~=
所有这些操作符产生的结果都是true或者false
'=='检查是否相等,'~='检查是否不等。这两个操作符可以作用于任意的两个值。如果要比较的两个值是不同的类型,那么Lua认为它们是不等的。否则,Lua根据它们的类型来比较它们是否相等。特例,nil只等于它本身。
Lua根据引用来比较table, userdata, function,也就是说,只有二者是同一个对象的时候,它们才相等。例如:
~~~
a = {}; a.x = 1; a.y = 0
b = {}; b.x = 1; b.y = 0
c = a
~~~
执行下上面的代码,你可以得到, a == c, 但是 a ~= b
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-09-06_57ce5eef395a4.PNG)
我们把<, >, <=, >=称为order operator。
order operator只能应用于数值和字符串。Lua根据字母表顺序来比较字符串的大小,字母表的顺序由本地设置决定。非数值和字符串的值只能比较相等或者不相等,不能用于order operator。
当比较两个不同类型的值时,一定要小心:记住"0"和0是不同的。除此以外,2<15显然是true的,但是"2"<"15"是false的(根据字母表顺序比较)。为了避免不一致的结果,当你将string和number混合使用到order operator时,例如 2<"15",Lua会产生一个runtime error。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-09-06_57ce5eef4c90d.PNG)
## 3.逻辑运算符
逻辑运算符有:and, or, not。跟控制结构(if,while等)类似,所有的逻辑操作符把false和nil认为是false,其他的任意值都为true。
and:如果第一个参数为false,返回第一个参数,否则返回第二个参数。
or :如果第一个参数为false,返回第二个参数,否则返回第一个参数。
~~~
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
~~~
'and'和'or'都是用short-cut计算方式,也即是说,只有在有需要的时候,才会去计算第二个参数的值。这样保证了类似(type(v)=="table" and v.tag=="h1")这样的表达式不会产生run-time error。(当v不是table类型时,Lua不会去计算v.tag的值)。
有用的Lua表达式:x = x or v,等价于:
if not x then x = v end
(加上x没有被赋值为false),如果x没有被设置,那么给它个默认值v。
另一个有用的Lua表达式:a and b or c,等价于C表达式 a ? b : c,(假设b不为false)。例如,我们可以用下面的语句来得到x和y的最大值。
max = (x >y) and x or y
当 x>y,第一个and表达式结果为第二个参数x,x总是true(因为x为一个数值),那么or表达式的结果为x or y,结果为x。
当 x true
print(not false) --> true
print(not 0) --> false
print(not not nil) --> false
~~~
## 4.连接操作符
Lua用'..'(两个点)来表示string连接符。如果它的任一个操作数为数字,Lua会把它转为string。
~~~
print("Hello " .. "World") --> Hello World
print(0 .. 1) --> 01
~~~
不要忘了,Lua中的string是常量。string连接操作符会创建一个新的string,不会对它的操作数有任何修改:
~~~
a = "Hello"
print(a .. " World") --> Hello World
print(a) --> Hello
~~~
## 5.优先级
Lua中的操作符优先级,如下表所示,从高到低
~~~
^
not # -(单目)
* / %
+ -
..
< > <= >= ~= ==
and
or
~~~
除了'^和'..',所有的双目操作符都是左关联的。因此,下表中,左面的表达式和右面的表达式是等价的:
~~~
a+i < b/2+1 <--> (a+i) < ((b/2)+1)
5+x^2*8 <--> 5+((x^2)*8)
a (a -(x^2)
x^y^z <--> x^(y^z)
~~~
在有疑问的时候,就用括号'()',这比查手册更方便和简单,而且,当你以后看代码的时候,你可能仍然有同样的疑问,显式地使用括号'()'会更直观一点。
## 6.表构造函数
构造函数是一个用来初始化表(table)的表达式。它是Lua的一个独特特性和很强大的功能之一。
最简单的构造函数是空构造函数,{},创建一个空table。构造函数也可以初始化数组,例如:
~~~
days = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
~~~
将day[1]初始化为"Sunday", day[2]为"Monday",注意,index是从1开始。
~~~
print(day[4]) --> Wednesday
~~~
Lua提供一个专门的语法去初始化record-like table,如下:
~~~
a = {x=10, y=20}
~~~
等价于
~~~
a = {}, a.x = 10; a.y = 20
~~~
不管我们用什么构造函数去创建一个table,我们都可以增加或减少结果中的field:
~~~
w = {x=0, y=0, label="console"}
x = {math.sin(0), math.sin(1), math.sin(2)}
w[1] = "another filed" -- add key 1 to table 'w'
x.f = w -- add key 'f' to table 'x'
print(w["x"]) --> 0
print(w[1]) --> another field
print(x.f[1]) --> another field
w.x = nil -- remove field 'x'
~~~
也就是说,table创建过程是相同的。构造函数只是将其初始化而已。
Lua每次使用构造函数,都会创建并初始化一个新的table。因此,我们可以用table来实现一个list:
~~~
list = nil
for line in io.lines() do
list = {next=list, value=line}
end
~~~
这些代码从stdin读入行,然后存入一个list中,顺序跟读入的顺序相反。list中的每个node有两个field:value,包含行的内容,next,指向下一个node。下面的代码遍历这个list,并且打印它的内容:
~~~
local l = list
while l do
print(l.value)
l = l.next
end
~~~
以上两段代码的示例:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-09-06_57ce5eef6281e.PNG)
(由于我们在栈上实现了list,打印的时候会是逆序的)。尽管实现很方便,但是我们在Lua程序中很少使用如上实现的list;List可以作为array得到更好的实现,我们会在后面再讲这个问题。
我们可在一个构造函数中同时使用record-style和list_style的初始化方式:
~~~
polyline = {color="blue", thickness=2, npoints=4,
{x=0, y=0},
{x=-10, y=0},
{x=-10, y=1},
{x=0, y=1}
}
~~~
上面的例子也说明了如何使用嵌套的构造函数来表示更复杂的数据结构。每一个polyline[i]都是一个table,表示一条记录(record):
~~~
print(polyline[2].x) --> -10 --此处要注意index 1是从哪里开始的,下面的示例may be helpful
print(polyline[4].y) --> 1
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-09-06_57ce5eef7782b.PNG)
这两种形式的构造函数(record-style和list_style)都有一些局限性。一些负数或者string不能作为标识符。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-09-06_57ce5eef8d0ff.PNG)
因此,Lua提供了一种更通用的格式。在这个格式中,我们显式的写出每一个[index]。
~~~
opnames = {["+"] = "add", ["-"] = "sub",
["*"] = "mul", ["/"] = "div"}
i = 20; s = "-"
a = {[i+0] = s, [i+1] = s..s, [i+2] = s..s..s}
print(opnames[s]) --> sub
print(a[22]) --> ---
~~~
这个规则有点麻烦,但是更方便灵活:record-style和list_style是这个通用格式的两个特例而已。
~~~
{x=0, y=0} <==> {["x"]=0, ["y"]=0}
{"r", "g", "b"} <==> {[1]="r", [2]="g", [3]="b"}
~~~
有时候,你想要你的array的index从0开始(Lua默认从1开始),用下面的方式就可以了:
~~~
days = {[0]="Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
~~~
现在,第一个值"Sunday", index为0 。它不会影响其他的field;"Monday"的index为1,因为它是构造函数中的第一个list值;其他的跟随。尽管实现很便利,但是不推荐这样用。Lua的很多内置函数都假设array的index从1开始,如果一个array从0开始,可能会在处理上有问题。
在最后一个元素后面,可以加逗号','。这个末尾的逗号','是可选的,但都是合法的:
~~~
a = {[1]="red", [2]="green", [3]="blue",}
~~~
通过这个便利性,程序在生成Lua table的时候不需要对最后一个元素做特殊处理。
最后,我们可以使用分号';'来代替逗号',',你可以在用';'来分割table中的不同属性的部分,例如:
~~~
{x=10, y=45; "one", "two", "three"}
~~~
水平有限,如果有朋友发现错误,欢迎留言交流。
';