学堂 学堂 学堂公众号手机端

Lua表达式总结

lewis 1年前 (2024-04-21) 阅读数 21 #技术


表达式用于表示值。Lua的表达式中可以包含数字常量、字面字符串、变量、一元和二元操作符及函数调用。另外有别于传统的是,表达式中还可以包括函数定义和table构造式。

1、算术操作符

Lua支持常规的算术操作符有:


操作符

作用

+

加法

-

减法

*

乘法

/

除法

^

指数

%

取模

-

负号

例如:

x^0.5               --计算x的平方根
x^(-1/3) --计算x立方根的倒数
a % b == a - floor(a/b)*b -- 取模操作符定义规则
x % 1 -- 结果是x的小数部分
x - x%1 --结果是x的整数部分
x-x%0.01 --x精确到小数点后两位的结果

x = math.pi
print(x - x%0.01) -- 3.14

2、关系操作符

Lua提供了如下关系操作符:

操作符

作用

​>​

大于

​<​

小于

​>=​

大于等于

​<=​

小于等于

​==​

等于

​~=​

不等于

所有这些操作符的运算结果都是true或false,操作符​​==​​​用于相等性测试,操作符​​~=​​用于不等性测试。这两个操作符可以应用于任意两个值。如果两个值具有不同的类型,Lua就认为他们是不相等的。否则Lua会根据它们的类型来比较两者。特别需要说明的是,nil只与其自身相等。

对于table、userdata和函数,Lua是作引用比较的,也就是说,只有当它们引用同一个对象时,才认为它们相等,例如:

a = {}
a.x = 1
a.y = 0

b = {}
b.x = 1
b.y = 0

c = a

-- 结果
a == c
a ~=

只能对两个数字或字符串作大小性比较。Lua是按照字母升序比较字符串的,具体的字母次序取决于对Lua的区域设置。例如,若将区域设置为"European Latin-1",那么会得到"acai"<“acaz”。而数字和字符串之外的其他类型只能进行相等性或不等性比较。

当对两个不同类型的值做比较时,要格外小心。“0”和0是不同的,此外 2<15显然是true,但“2”<"15"却是false(因为是按照字母次序来比较的)。为了避免类型不一致的比较,Lua会在遇到字符串和数字的大小比较时引发一个错误,例如2<"15"就会导致这种错误。


3、逻辑操作符

Lua提供了如下的逻辑操作符:

操作符

作用

and


or


not


与条件控制语句一样,所有的逻辑操作符将false和nil视为假,而将其他的任何东西视为真。

Lua与C语言中的逻辑操作符不同,C语言中是返回true或false,而Lua中是返回操作数

对于操作符and来说,如果它的第一个操作数为假,就返回第一个操作数,不然返回第二个操作数;

对于操作符or来说,如果它的第一个操作数为真,就返回第一个操作数,不然返回第二个操作数;

and和or都使用“短路求值”,也就是说,它们只会在需要是采取评估第二个操作数。

-- 在没有设置x的时候,将其设为一个默认值v
x = x or v
-- 等价于
if not x then
x = v
end

-- a and b or c 类似于C中的三目运算符a ? b : c
max = (x>y) and x or

若x>y,则and的第一个操作数为真,那么and运算的结果就是其第二个操作数x,而x是一个永远为真的表达式(因为它是一个数字)。然后or运算的结果就是其第一个操作数x。

若x>y为假的时候,and表达式为假,因此or运算的结果就是其第二个操作数y。

操作符not永远只返回true或false

print(not nil)    --true
print(not false) --true
print(not 0) --false
print(not not nil) --false

4、字符串连接

Lua中连接两个字符串,可以使用操作符"…"(两个点)。如果其任意一个操作数是数字的话,Lua会将这个数字转换成一个字符串(数字和字符串连接符用空格隔开),例如:

print("Hello" .. "World")   -- Hello World
print(0 .. 1) -- 01

Lua中的字符串是不可变的值。连接操作符只会创建一个新字符串,而不会对其操作数进行任何修改:

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 < y and y <= z <--> (a < y) and (y <= z)
-x^2 <--> -(x^2)
x^y^z <--> x^(y^z)

若不确定某些操作符的优先级时,就应显示地用括号来指定所期望的运算次序。


6、table构造式

构造式是用于创建和初始化table的表达式,这是Lua特有的一种表达式,并且也是Lua中最有用、最通用的机制之一。

最简单的构造式就是空构造式,用于穿件一个空table。构造式还可以用于初始化数组。例如:

days = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}

print(days[4]) -- Wednesday

Lua还提供了一种特殊的语法用于初始化记录风格的table:

a = {x=10,y=20}
-- 等价于
a = {}
a.x = 10
a.y = 20

无论使用哪种方式来创建table,都可以在table创建之后添加或删除其中的某些字段:

w = {x=0,y=0,label="console"}
x = {math.sin(0),math.sin(1),math.sin(2)}
w[1] = "another field" -- 添加key 1到table w
x.f = w -- 添加key "f"到table x
print(w["x"]) -- 0
print(w[1]) -- another field
print(x.f[1]) -- another field
w.x = nil -- 删除字段"x"

用table实现一个链表:

list = nil
for line in io.lines() do
list = {value = line, next = list}
end

这段代码从标准输入中读取每行的内容,然后将每行按相反的次序存储到一个链表中。链表的每个节点都是一个table,table中含有两个字段:value(每行的内容)和next(指向下一个结点的引用)。以下代码遍历了该链表,并打印其内容:

local l = list
while l do
print(l.value)
l = l.next
end

上例虽有一定的教学意义,但在真是的Lua程序中很少会用到链表,列表数据一般是通过数组来实现的。

将记录风格的初始化与列表风格的初始化混合在一个构造式中使用:

polyline = {
color="blue", thickness=2, npoints=4,
{x=0, y=0},
{x=-10, y=0},
{x=-10, y=1},
{x=0, y=1}
}

print(polyline[2].x) -- -10
print(polyline[4].y) -- 1

上例演示了如何通过嵌套的构造式来表示复杂的数据结构,每个polyline[i]元素都是一个table,表示一条记录,其中{x=0, y=0}是polyline[1]。

这两种风格的构造式各有其限制。例如,不能使用负数的索引,也不能用运算符作为记录的字段名。为了满足这些要求,Lua还提供了一种更通用的格式。这种格式允许在方括号之间,显示地用一个表达式来初始化索引值:

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]) --> ---

这种语法看似烦琐一点,但却非常灵活。无论是列表风格的初始化,还是记录风格的初始化,其实都是这种通用语法特例。构造式{x=0, y=0}等价于{[“x”]=0, [“y”]=0},构造式{“r”,“g”,“b”}等价于{[1]=“r”, [2]=“g”, [3]=“b”}。

对于某些情况如果真的需要以0作为一个数组的起始索引的话,通过这种语法也可以轻松做到:

days = {[0]="Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}

现在第一个值“Sunday”的索引就为0了。这个索引0并不影响其他元素,“Monday”照常索引为1,因为它是构造式中列表风格的第一个值,后续其他值的索引依次递增。但无论是否使用这种初始化语法,都不推荐在Lua中以0作为数组的起始索引。大多数内建函数都假设数组起始于索引1,若遇到以索引0开始的数组,它们就无法进行正确地处理了。

可以在最后一个元素后面写一个逗号,这个特性是可选的,也是合法的:
a = {[1]="red", [2]="green", [3]="blue", }
在构造式中还可以用分号代替逗号,通常会将分号用于分隔构造式中不同的成分,例如将列表部分与记录部分明显地区分开:
{x=10, y=45; "one", "two", "three"}


版权声明

本文仅代表作者观点,不代表博信信息网立场。

热门