Lua 编程/表达式
如前所述,表达式是具有值的代码片段,可以进行评估。它们不能直接执行(函数调用除外),因此,仅包含以下代码(由表达式组成)的脚本将是错误的
3 + 5
-- The code above is erroneous because all it contains is an expression.
-- The computer cannot execute '3 + 5', since that does not make sense.
代码必须由一系列语句组成。这些语句可以包含表达式,这些表达式将是语句必须操作或用于执行指令的值。
本章中的一些代码示例不构成有效的代码,因为它们只包含表达式。在下一章中,将介绍语句,并且可以开始编写有效的代码。
评估表达式就是计算它以找到它的值。给定表达式评估到的值可能因上下文而异,因为它可能取决于环境和堆栈级别。这个值有时将是一个数字,有时是文本,有时是许多其他数据类型中的任何一个,这就是为什么它被称为具有类型。
在 Lua 中,以及在一般的编程中,表达式通常由一个或多个值和零个或多个运算符组成。某些运算符只能与某些类型一起使用(例如,试图除文本是不合逻辑的,而除数字则有意义)。运算符有两种:一元运算符和二元运算符。一元运算符是只接受一个值的运算符。例如,一元运算符 - 只接受一个数字作为参数:-5、-3、-6 等。它接受一个数字作为参数并对该数字取反。然而,二元运算符 - 与之不同,它接受两个值并将第二个值从第一个值中减去:5 - 3、8 - 6、4 - 9 等。
可以使用 type
函数以字符串形式获取数字的类型
print(type(32425)) --> number
数字通常代表数量,但它们可以用于许多其他用途。Lua 中的数字类型在大多数情况下与实数相同。数字可以构造为整数、小数、小数指数,甚至以 十六进制 形式。以下是一些有效的数字
- 3
- 3.0
- 3.1416
- 314.16e-2
- 0.31416E1
- 0xff
- 0x56
Lua 中的数字运算符如下
操作 | 语法 | 描述 | 示例 | 结果 |
---|---|---|---|---|
算术否定 | -a | 更改 a 的符号并返回该值 | -3.14159 | |
加法 | a + b | 返回 a 和 b 的总和 | 5.2 + 3.6 | 8.8 |
减法 | a - b | 从 a 中减去 b 并返回结果 | 6.7 - 1.2 | 5.5 |
乘法 | a * b | 返回 a 和 b 的乘积 | 3.2 * 1.5 | 4.8 |
指数运算 | a ^ b | 返回 a 的 b 次方,或 a 被 b 乘方的结果 | 5 ^ 2 | 25 |
除法 | a / b | 用 b 除以 a 并返回结果 | 6.4 / 2 | 3.2 |
向下取整除法 | a // b | 用 b 除以 a 并返回结果的整数部分 | 6.4 // 2 | 3 |
模运算 | a % b | 返回 a 被 b 除的余数 | 5 % 3 | 2 |
您可能已经知道所有这些运算符(它们与基本数学运算符相同),除了最后一个。最后一个称为模运算符,它简单地计算一个数字被另一个数字除的余数。例如,5 % 3 将返回 2 作为结果,因为 2 是 5 被 3 除的余数。模运算符不像其他运算符那么常见,但它有许多用途。
在 Lua 5.3 中添加了数字的一个新子类型:整数。数字可以是整数或浮点数。浮点数类似于上面描述的数字,而整数是没有任何小数部分的数字。浮点数除法 (/
) 和指数运算始终将其操作数转换为浮点数,而所有其他运算符如果其两个操作数都是整数,则返回整数。在其他情况下,除了向下取整除法运算符 (//
) 之外,结果将是浮点数。
Nil 是值 nil 的类型,其主要属性是与任何其他值不同;它通常表示缺少有用的值。以下是一些具有 nil 值的事物的示例
- 您在为变量分配值之前访问它们的变量的值
- 尝试在变量作用域之外访问变量时得到的值
- 表中任何未分配的键的值
tonumber
返回的值,如果它无法将字符串转换为数字
更高级一点,有意分配 nil 值会删除对变量或表的引用,并允许 垃圾收集器 重新分配其内存。
布尔值可以是 true 或 false,但不能是其他值。在 Lua 中,它写为 true
和 false
,它们是保留关键字。重要的是要注意 nil
是一个不同的数据类型,如前所述。and
、or
、not()
通常与布尔值相关联,但可以在 Lua 中与任何数据类型一起使用。
操作 | 语法 | 描述 |
---|---|---|
布尔值否定 | not a | 如果 a 为 false 或 nil,则返回 true。否则,返回 false。 |
逻辑连接 | a and b | 如果第一个参数为 false 或 nil,则返回第一个参数。否则,返回第二个参数。 |
逻辑析取 | a or b | 如果第一个参数既不是 false 也不是 nil,则返回第一个参数。否则,返回第二个参数。 |
本质上,not
运算符只是对布尔值取反(如果它是 true 则使其变为 false,如果它是 false 则使其变为 true),and
运算符如果两者都为 true 则返回 true,否则返回 false,而 or
运算符如果其中任何一个参数为 true 则返回 true,否则返回 false。然而,这并不是它们的工作原理,因为它们的实际工作原理如上表所述。在 Lua 中,值 false 和 nil 在逻辑表达式中都被视为 false,而其他所有内容都被视为 true(即使是 0 和空字符串)。
下一章中介绍的关系运算符 (<
、>
、<=
、>=
、~=
、==
) 不一定将布尔值作为操作数,但始终会返回布尔值作为结果。
这可能难以理解。为了更清楚地说明,以下是一些真值表或表达式-结果对。这里 x
可以是 nil
、true
或 false
表达式 | 结果 |
---|---|
true and x
|
x
|
false and x
|
false
|
nil and x
|
nil
|
true or x
|
true
|
false or x
|
x
|
nil or x
|
x
|
这有点违反直觉,这意味着
表达式 | 结果 |
---|---|
false and nil
|
false
|
nil and false
|
nil
|
此外,
表达式 | 结果 |
---|---|
false == nil nil == false
|
false
|
nil and false
|
nil
|
表达式 | 结果 |
---|---|
not(false) not(nil)
|
true
|
not(true)
|
false
|
字符串是字符的序列,可以用来表示文本。它们可以在 Lua 中用双引号、单引号或长括号表示,这些在之前的关于注释的部分中已经介绍过(需要注意的是,注释和字符串除了都可以用长括号分隔之外,没有其他共同点,注释需要在长括号前面加上两个连字符)。没有用长括号括起来的字符串只持续一行。因此,要创建一个包含多行的字符串而不用长括号,唯一的方法是使用转义序列。这也是在某些情况下插入单引号或双引号的唯一方法。转义序列由两部分组成:转义字符,在 Lua 中总是反斜杠 ('\'),以及一个标识要转义的字符的标识符。
转义序列 | 描述 |
---|---|
\n | 换行符 |
\" | 双引号 |
\' | 单引号(或撇号) |
\\ | 反斜杠 |
\t | 水平制表符 |
\### | ### 必须是一个从 0 到 255 的数字。结果将是对应的ASCII字符。 |
当直接将字符放入字符串会导致问题时,会使用转义序列。例如,如果一个字符串包含在双引号中,并且必须包含双引号,那么就需要将字符串包含在不同的字符中,或者对双引号进行转义。在用长括号分隔的字符串中转义字符是不必要的,这对所有字符都适用。用长括号分隔的字符串中的所有字符都将按原样处理。%
字符在字符串模式中用于转义魔法字符,但术语转义在另一种情况下使用。
"This is a valid string."
'This is also a valid string.'
"This is a valid \" string 'that contains unescaped single quotes and escaped double quotes."
[[
This is a line that can continue
on more than one line.
It can contain single quotes, double quotes and everything else (-- including comments). It ignores everything (including escape characters) except closing long brackets of the same level as the opening long bracket.
]]
"This is a valid string that contains tabs \t, double quotes \" and backlashes \\"
"This is " not a valid string because there is an unescaped double quote in the middle of it."
为了方便起见,如果一个开始的长字符串括号紧跟着一个换行符,那么这个换行符将被忽略。因此,以下两个字符串是等价的
[[This is a string
that can continue on many lines.]]
[[
This is a string
that can continue on many lines.]]
-- Since the opening long bracket of the second string is immediately followed by a new line, that new line is ignored.
可以使用一元长度运算符 ('#') 获取字符串的长度,以数字形式表示
print(#("This is a string")) --> 16
连接
[edit | edit source]—维基百科, 连接
Lua 中的字符串连接运算符用两个点 ('..') 表示。下面是一个连接 "snow" 和 "ball" 并打印结果的例子
print("snow" .. "ball") --> snowball
这段代码将连接 "snow" 和 "ball" 并打印结果。
其他类型
[edit | edit source]Lua 中的四种基本类型(数字、布尔值、nil 和字符串)已经在之前的部分中介绍过,但还有四种类型没有介绍:函数、表、userdata 和线程。函数是可以调用的代码片段,可以接收值并返回结果。表是可以用于数据操作的数据结构。userdata是在 Lua 嵌入的应用程序内部使用的,用于允许 Lua 通过应用程序控制的对象与该程序进行通信。最后,线程由协程使用,协程允许多个函数同时运行。这些将在后面介绍,所以只需要记住还有其他的数据类型。
字面量
[edit | edit source]字面量是在源代码中表示固定值的表示法。除了线程和 userdata 之外,所有值都可以用字面量在 Lua 中表示。字符串字面量(计算结果为字符串的字面量)例如,由字符串必须表示的文本组成,这些文本包含在单引号、双引号或长括号中。另一方面,数字字面量由它们用十进制表示法(例如:12.43
)、科学计数法(例如:3.1416e-2
和 0.31416E1
)或十六进制表示法(例如:0xff
)表示的数字组成。
强制转换
[edit | edit source]强制转换是指将一种数据类型的值转换为另一种数据类型的值。Lua 在字符串和数字值之间提供了自动强制转换。对字符串应用的任何算术运算都将尝试将该字符串转换为数字。相反,只要需要字符串而使用的是数字,数字就会被转换为字符串。这适用于 Lua 运算符和默认函数(与语言一起提供的函数)。
print("122" + 1) --> 123
print("The number is " .. 5 .. ".") --> The number is 5.
数字到字符串和字符串到数字的强制转换也可以使用 tostring
和 tonumber
函数手动完成。前者接受一个数字作为参数并将其转换为字符串,而后者接受一个字符串作为参数并将其转换为数字(可以在第二个参数中选择一个与默认十进制不同的基数)。
位运算
[edit | edit source]从 Lua 5.3 开始,提供了位运算符来对二进制数字(位模式)进行运算。这些运算符不像其他运算符那样经常使用,所以如果你不需要它们,可以跳过这一节。
Lua 中的位运算符始终对整数进行运算,如果需要会将操作数转换为整数。它们也返回整数。
按位与运算(运算符为 &
)对两个二进制表示形式的相同长度的每对位执行逻辑合取。例如,5 & 3
计算结果为 1。我们可以通过查看这些数字的二进制表示来解释这一点(下标用于表示基数)
如果 5 和 3 的二进制表示中给定位置上的位都是 1(例如,最后一位),那么结果中该位置上的位将是 1;在所有其他情况下,它将是 0。
按位或运算(运算符为 |
)的工作原理与按位与相同,只是它在执行逻辑合取的地方执行逻辑析取。因此,5 | 3
将计算结果为 7
在这里,我们可以看到,只有当两个操作数的二进制表示在该位置上都为 0 位时,最终结果中该位置上的位才为 0。
按位异或运算(运算符为 ~
)的工作原理与其他两个运算符类似,但在给定位置上,只有当一个操作数的位为 1,而另一个操作数的位不为 1 时,最终位才为 1。
这与前面的例子相同,但我们可以看到结果中的最后一位是 0 而不是 1,因为两个操作数的最后一位都是 1。
按位取反运算(运算符为 ~
)对唯一操作数的每一位执行逻辑否定,这意味着每个 0 都变成 1,每个 1 都变成 0。因此,~7
将计算结果为 -8
这里,第一个位在结果中变为 1 是因为在操作数中它是 0,其他位变为 0 是因为它们都是 1。
除了这些按位运算符之外,Lua 5.3 还支持算术位移。左侧的左移,运算符为<<
,左侧的说明是将所有位向左移动,移动的位数与第二个操作数对应。右侧的右移,运算符为>>
,右侧的说明是相同方向的反向操作。
运算符优先级在 Lua 中的工作方式与在数学中通常相同。某些运算符将在其他运算符之前计算,并且可以使用括号任意更改执行运算的顺序。运算符计算的优先级在下面的列表中,从最高优先级到最低优先级。其中一些运算符尚未讨论,但它们都将在本书的某个时间点介绍。
- 指数运算:
^
- 一元运算符:
not
,#
,-
,~
- 2 级数学运算符:
*
,/
,//
,%
- 1 级数学运算符:
+
,-
- 连接:
..
- 位移:
<<
,>>
- 按位 AND:
&
- 按位 XOR:
~
- 按位 OR:
|
- 关系运算符:
<
,>
,<=
,>=
,~=
,==
- 布尔 AND:
and
- 布尔 OR:
or
您可以回答一些问题,以验证您是否理解本章中的内容。请注意,找到某些问题的答案可能需要您拥有本章中未介绍的知识。这是正常的:测验是学习体验的一部分,它们可以介绍书中其他地方没有的信息。