Lua中基本的数据类型、表达式与流程控制语句讲解
Lua提供8种基本类型:nil、boolean、number、string、table等,只有false和nil为假,number为双精度浮点,字符串不可变,table索引从1开始。表达式包含算术、关系、逻辑运算符及连接符..,支持自动类型转换。控制结构有if、while、repeat-until、数字型for和泛型for,均以end或until结尾。
Lua 作为一种动态类型语言,其类型系统设计得非常灵活,没有传统静态语言那种繁琐的类型定义语法。今天我们来系统梳理一下 Lua 的 8 种基础类型,以及在实际使用中容易被忽略的细节。
1. Lua 类型
1.1 基本类型
Lua 一共提供了 8 种基础类型:nil(空)、boolean(布尔)、number(数字)、string(字符串)、userdata(自定义类型)、function(函数)、thread(线程)、table(表)。
通过内置的 type 函数,你可以随时获取任意值的类型名称(返回字符串)。举个例子:print(type(print)) 会输出 "function";而 print(type(type(X))) 则输出 "string" —— 无论 X 是否定义,type(X) 始终返回一个字符串,所以外层 type 拿到的永远是一个字符串类型。
1.2 各类型要注意的地方
关于 boolean:
在 Lua 中,只有 false 和 nil 被视为“假”,其他所有值(包括数字 0 和空字符串)都被视为“真”。这一点和很多语言不太一样,写条件判断时要特别留意。
关于 number:
Lua 的 number 类型实际上就是双精度浮点数(double),整数也用它来表示。所以不用担心精度问题——只要在 double 能精确表达的整数范围内,运算结果都是准确的。
关于 string:
字符串是不可变的,你不能像 C 语言那样直接修改某个字符。如果需要修改,必须通过 string 库创建新字符串。字符串可以用单引号或双引号界定,也支持 C 风格的转义序列(比如 \n 换行)。
一个比较方便的特性是,你可以用双方括号 [[内容]] 来定义多行字符串,其中的换行符会被原样保留。更妙的是,如果在左边方括号间加入任意数量的等号(比如 [===[),那么结束标记也必须使用相同数量的等号(比如 ]===])。这样一来,字符串里即使包含原始的双括号内容,也不需要转义。这个技巧同样适用于注释:--[==[ 注释内容 ]==]。
Lua 在运行时支持数字与字符串的自动转换。例如 print("10" + 1) 输出 11,print(10 .. 20) 输出 "1020"(.. 是字符串连接符,注意数字和 .. 之间要加空格以避免歧义)。不过建议尽量少依赖自动转换,显式使用 tonumber(x) 和 tostring(x) 更安全。tonumber 转换失败时返回 nil。要获取字符串长度,可以在字符串前加 # 运算符,比如 print(#"length") 输出 6。
关于 table:
Table 是 Lua 中最核心的数据结构,它可以看作一种具有特殊索引方式的数组,允许使用除 nil 外的任何类型作为索引。Table 的增删都通过自动内存管理完成,你不需要手动释放。注意,table 是一个“对象”,变量只持有对它的引用——比如 a = {} 创建了一个 table,然后 b = a 会让 b 和 a 指向同一个 table。如果某个元素没有初始化,其值就是 nil,而将某个元素赋值为 nil 就可以删除它。
访问 table 时有两种等价写法:p["age"] 等价于 p.age。但 p.age 和 p[age] 完全不是一回事——后者中的 age 是一个变量。
# 运算符可以返回数组的最后一个索引(即大小)。例如 print(a[#a]) 打印最后一个元素,a[#a+1] = io.read() 则是在末尾追加一个值。不过要注意,Lua 把 nil 当作数组的结束标志,如果数组中间含有 nil,# 会认为第一个 nil 就是结尾。因此,尽量避免对存在“空隙”的数组使用 #。
需要强调的是,Lua 中 table 默认索引从 1 开始,而不是 C 语言的 0,这一点在后续使用中要特别留意。
Function、userdata 和 thread 类型我们后面再详细展开。
2. 表达式
2.1 算术操作符
常规操作符包括:+(加)、-(减)、*(乘)、/(除)、^(指数)、%(取模)。其中 ^ 可以用于任意实数,比如 x ^ (-1/3) 可以计算 x 的立方根倒数。% 的用法也很灵活:x % 1 得到 x 的小数部分,x - x % 0.01 则得到 x 精确到小数点后两位的值。
2.2 关系操作符
标准的关系操作符有:<(小于)、>(大于)、<=(小于等于)、>=(大于等于)、==(相等)、~=(不等)。== 和 ~= 可以用于任意两个值,如果类型不同则不相等。相同类型则进行正常比较(nil 只与自身相等)。对于 table 这类对象,比较的是它们的引用。
2.3 逻辑操作符
逻辑操作符有 and、or 和 not。对于 and,如果第一个操作数为假,就返回第一个操作数,否则返回第二个操作数。对于 or,如果第一个操作数为真,就返回第一个操作数,否则返回第二个。比如 print(4 and 5) 输出 5,print(false or 5) 输出 5。
利用这个特性可以写出很简洁的代码:x = x or v 相当于给 x 设置默认值 v(如果 x 未初始化)。而 (a and b) or c 则类似三元运算符 a ? b : c,比如 max = (x > y) and x or y。
2.4 字符串连接符
用两个点 .. 来连接字符串,如果其中任一个操作数是数字,Lua 会自动将其转换为字符串。例如 print(0 .. 1) 输出 "01"。注意连接操作会创建一个新字符串,原操作数不会被修改。
Lua 运算符的优先级顺序如下图所示:

2.5 Table 构造式
除了用 {} 创建空 table,也可以在创建时直接初始化。例如 days = {"S", "M", "T"} 会创建一个以数字为索引的列表,默认索引从 1 开始。再如 point = {x = 10, y = 20} 则等价于 point.x = 10; point.y = 20。这两种方式可以混用,甚至可以用分号代替逗号来明显分隔列表部分和记录部分:
polyline = {color = "blue"; {x = 0, y = 0}, {x = 10, y = 10}, {x = 20, y = 30}}
print(polyline[2].x) -- 输出 10
更通用的初始化格式允许在方括号间声明索引值,比如 opnames = {["+"] = "add", ["-"] = "minus"}。如果你确实需要以 0 作为数组起始索引,可以这样写:days = {[0] = "S", "M", "T"}。但强烈建议不要这样做——Lua 的大多数内建函数都假设数组起始于索引 1,硬要打破这个惯例只会给自己添麻烦。
3. 顺序结构
3.1 赋值
除了常规的 = 赋值,Lua 还支持多重赋值,变量和值之间用逗号分隔。Lua 会自动调整等号右边的值个数,使其与左边变量个数匹配:如果值不够,多余的变量会被赋为 nil;如果值多了,多余的值会被丢弃。例如 a, b = 10, 2 * x,或者经典的交换 x, y = y, x。多重赋值最常见的用途就是交换变量,或接收函数返回的多个值。
3.2 局部变量与程序块
和很多语言一样,Lua 提供了局部变量,用关键字 local 声明。局部变量的作用域仅限于声明它的程序块(比如控制结构的执行体、函数体等)。注意,在交互模式中,每行输入本身就是一个程序块,所以单句 local 声明是无效的。如果想显式地界定一个块,可以用一对 do-end 关键字包裹起来,这在严格控制变量作用域时非常有用。
有一种常见的写法:local foo = foo。这看起来有点奇怪,但实际上是用全局变量 foo 的值初始化一个同名的局部变量。这样做的目的是“缓存”当前值,避免后续其他函数修改全局变量时产生影响。
4. 控制结构
Lua 的所有控制结构都有显式的终止符:if、for、while 以 end 结尾,repeat 以 until 结尾。
4.1 if then else(elseif) 语句
标准格式如下:
if <条件1> then
<内容1>
elseif <条件2> then
<内容2>
else
<内容3>
end
Lua 没有 switch 语句,所以一连串 elseif 是很常见的写法。
4.2 while 和 repeat 语句
while 的格式:while <条件> do <循环体> end,先判断条件,再执行循环体。
repeat 的格式:repeat <循环体> until <条件>,先执行一次循环体,再判断条件。注意,在 repeat 循环体内声明的局部变量,其作用域包括循环的条件测试部分——也就是说你可以在条件中引用循环体内刚刚声明的局部变量。
4.3 数字型 for 语句
格式:for var = exp1, exp2, exp3 do <循环体> end。变量 var 从 exp1 开始,每次增加 exp3,直到超过 exp2 为止。exp3 是可选的,默认为 1。如果想让循环无限进行,可以将 exp2 设为 math.huge。
有几个细节值得注意:三个表达式只在循环开始前一次性求值,后续不会重新计算。控制变量 var 会被自动声明为 for 语句的局部变量,仅在循环体内可见,循环结束后就不可用了。
4.4 泛型 for 语句
泛型 for 通过迭代器遍历所有值,最典型的用法是:
for i, v in ipairs(tableA) do
print(v)
end
这里 i 是索引,v 是对应的元素值。ipairs 用于遍历数组部分(从索引 1 开始连续无空隙的序列),pairs 则用于遍历整个 table 的所有键值对,string.gmatch 可以迭代字符串中的单词。
泛型 for 的一个经典应用是创建“逆向 table”——即将原 table 的键和值互换,生成一个新 table。
4.5 break 和 return 语句
这两个语句都用于跳出当前语句块:break 结束一层循环,return 结束一个函数的执行。如果需要在函数开头就提前返回(比如调试时),可以用一个显式的 do 块来包裹 return 语句,否则 return 必须放在函数的最外层或 do-end 块内。例如:
function foo()
do return end
-- 后续代码不会执行
end
Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















