基本语法

标识符

Lua 标识符用于定义一个变量。

Lua 是一个区分大小写的编程语言。

最好不要使用下划线加大写字母的标识符(比如 _VERSION),因为Lua的保留字也是这样的。

Lua 不允许使用特殊字符@, $, 和 % 来定义标识符。

1
2
3
name = "Jerry"
_temp = 1
Abc = true

关键词

以下列出了 Lua 的保留关键词,保留关键字不能作为常量或变量或其他用户自定义标示符:

and break do else
elseif end false for
function if in local
nil not or repeat
return then true until
while goto

一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。

变量

lua默认变量是全局变量

Lua 是动态类型语言,变量无需声明类型,但实际存储时会区分以下类型:

类型 示例 特点
nil local x 未赋值的默认值
boolean local b = true true/false 两个值
number local n = 3.14 不区分整数和浮点数
string local s = "Lua" 不可变,采用高效哈希存储
table local t = {} 唯一数据结构,功能强大
function local f = print 一等公民,可存储在变量中

类型检测:

1
2
3
print(type(undefined_var))  --> nil
print(type(1 == 1)) --> boolean
print(type(2^100)) --> number (自动切换为浮点)

变量作用域规则

全局变量

  • 直接赋值创建(无需声明)
  • 生命周期持续到脚本结束
  • 存储在 _G 全局表中
1
2
playerName = "阿尔萨斯"  -- 全局变量
print(_G["playerName"]) --> 阿尔萨斯(可通过_G访问)

局部变量(推荐)

  • 使用 local 关键字声明
  • 作用域限定在声明块内(代码块/函数/控制结构)
  • 优先访问局部变量
1
2
3
4
5
do
local itemCount = 5 -- 仅在此代码块有效
print(itemCount) --> 5
end
print(itemCount) --> nil(已超出作用域)

常见作用域示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 文件级局部变量(整个文件可见)
local MAX_LEVEL = 100

function CreatePlayer()
-- 函数局部变量
local defaultHP = 200
return { hp = defaultHP }
end

for i = 1, 3 do
-- 循环局部变量(每次迭代独立)
local temp = i * 2
print(temp) --> 2, 4, 6
end

闭包中的变量

1
2
3
4
5
6
7
8
9
10
11
function MakeCounter()
local count = 0 -- 闭包变量(生命周期延长)
return function()
count = count + 1
return count
end
end

local c1 = MakeCounter()
print(c1()) --> 1
print(c1()) --> 2(保持count状态)

多次赋值

1
2
3
4
5
6
7
8
9
10
11
-- 基本用法
local x, y = 10, 20
x, y = y, x -- 交换变量值

-- 不足补nil,多余忽略
local a, b, c = 1, 2
print(c) --> nil

-- 函数返回接收
function GetCoords() return 10, 20 end
local posX, posY = GetCoords()

字符串

表示方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 单引号字符串
local s1 = 'Hello'

-- 双引号字符串
local s2 = "World"

-- 多行字符串
local s3 = [[
这是
多行
字符串
]]

-- 转义字符
local s4 = "Line1\nLine2\tTabbed"

字符串连接

1
2
3
4
5
6
local name = "Alice"
local greeting = "Hello, " .. name .. "!" --> "Hello, Alice!"

-- 大量连接推荐使用 table.concat
local parts = {"This", "is", "a", "sentence"}
local sentence = table.concat(parts, " ") --> "This is a sentence"

字符串函数

基础函数

函数 示例 结果 说明
string.len() string.len("Lua") 3 字符串长度
string.lower() string.lower("Lua") “lua” 转小写
string.upper() string.upper("Lua") “LUA” 转大写
string.reverse() string.reverse("Lua") “auL” 反转字符串
string.rep() string.rep("a", 3) “aaa” 重复字符串

子字符串操作

1
2
3
4
5
6
7
8
9
local s = "Programming in Lua"

-- 截取子串
print(string.sub(s, 5, 8)) --> "gram"
print(s:sub(-3)) --> "Lua" (负索引从末尾开始)

-- 查找模式
print(string.find(s, "in")) --> 11 12 (返回开始和结束位置)
print(s:find("Lua")) --> 16 18 (方法调用形式)

字符串格式化

1
2
3
4
5
6
7
8
-- 基本格式化
print(string.format("Pi = %.2f", math.pi)) --> "Pi = 3.14"

-- 日期格式化
local date = string.format("%04d-%02d-%02d", 2023, 5, 20) --> "2023-05-20"

-- 对齐格式化
print(string.format("%-10s|%5d", "Lua", 5)) --> "Lua | 5"

常用格式说明符

说明符 示例 输出示例 说明
%.nf %.2f “3.14” 浮点数,保留n位小数
%nd %04d “0012” 整数,至少n位数字(不足补0)
%ns %10s “ Hello” 字符串,至少n个字符宽度(默认右对齐)
%-ns %-10s “Hello “ 字符串,左对齐
%e %.2e “3.14e+00” 科学计数法
%g %.4g “3.142” 自动选择%f或%e的更紧凑形式
%% "50%%" “50%” 百分号转义

运算符

运算符类型 运算符 示例 说明 优先级
算术运算符 + 5 + 3 → 8 加法 4
- 7 - 2 → 5 减法 4
* 3 * 4 → 12 乘法 3
/ 10 / 2 → 5.0 除法(始终返回浮点数) 3
// (Lua 5.3+) 10 // 3 → 3 整除(向下取整) 3
% 10 % 3 → 1 取模 3
^ 2^3 → 8 幂运算(优先级高于乘除) 2
- (一元) -5 → -5 负号 5
关系运算符 == 5 == 5 → true 相等比较(不自动转换类型) 6
~= 5 ~= 3 → true 不等比较 6
< 3 < 5 → true 小于 6
> 5 > 3 → true 大于 6
<= 3 <= 3 → true 小于等于 6
>= 5 >= 5 → true 大于等于 6
逻辑运算符 and true and false → false 逻辑与(短路求值) 7
or true or false → true 逻辑或(短路求值) 8
not not true → false 逻辑非 5
连接运算符 .. "Hello".."World" → “HelloWorld” 字符串连接(会强制数字转字符串) 1
长度运算符 # #"Lua" → 3 获取字符串长度/数组元素个数(忽略非连续数字键) 5
位运算符 (Lua 5.3+) & 5 & 3 → 1 按位与 4
` ` `5 3` → 7
~ ~5 → -6 按位非 5
<< 1 << 2 → 4 左移 4
>> 8 >> 1 → 4 右移 4
特殊运算符 : obj:method() 方法调用语法糖(自动传递self) -
[] tbl[key] 表索引访问 -

条件语句

if-then-else 基础结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- 基本形式
if 条件 then
-- 条件为真时执行
end

-- 带else分支
if score >= 90 then
print("优秀")
else
print("及格")
end

-- 多条件判断
if temperature > 30 then
print("炎热")
elseif temperature > 20 then
print("舒适")
elseif temperature > 0 then
print("凉爽")
else
print("寒冷")
end

条件表达式特点:

  • Lua 中只有 falsenil 视为假,其他值都为真
  • 包括 0""{} 都视为真值
  • 支持短路求值
1
2
3
4
5
-- 短路求值示例
local name = username or "匿名用户" -- 如果username为nil则使用默认值

-- 安全访问嵌套表
local value = config and config.settings and config.settings.volume or 50

循环语句

while循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- 基本形式
local i = 1
while i <= 5 do
print("计数:", i)
i = i + 1
end

-- 带break的循环
local found = false
while not found do
local num = math.random(10)
if num == 7 then
print("找到7了!")
found = true
end
-- 或者使用break直接退出
-- if num == 7 then break end
end

repeat-until 循环

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 至少执行一次的循环
local input
repeat
print("请输入yes继续:")
input = io.read()
until input == "yes"

-- 与while的区别
local x = 10
repeat
x = x + 1
print(x) -- 会先执行一次
until x > 10 -- 条件为真时退出

for循环

数值for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 基本形式 (包含结束值)
for i = 1, 5 do
print(i) -- 输出1,2,3,4,5
end

-- 指定步长
for i = 10, 1, -2 do -- 从10到1,步长-2
print(i) -- 输出10,8,6,4,2
end

-- 浮点数步长(Lua 5.2+)
for x = 0, 1, 0.2 do
print(string.format("%.1f", x)) -- 0.0,0.2,0.4,0.6,0.8,1.0
end

泛型for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-- 遍历数组(ipairs)
local colors = {"红","绿","蓝"}
for i, color in ipairs(colors) do
print(i, color) -- 1 红, 2 绿, 3 蓝
end

-- 遍历表(pairs)
local player = {name="Alice", level=30, class="Mage"}
for k, v in pairs(player) do
print(k..": "..v) -- 输出所有键值对(顺序不确定)
end

-- 自定义迭代器
function square(max)
local i = 0
return function()
i = i + 1
if i <= max then
return i, i*i
end
end
end

for num, sq in square(3) do
print(num, sq) -- 1 1, 2 4, 3 9
end

pairs 和 ipairs对比

ipairs (Indexed Pairs)

特点:

  • 顺序遍历:从索引 1 开始,按 连续数字索引 依次遍历(1, 2, 3…)
  • 终止条件:遇到第一个 nil 值时停止
  • 忽略非数字键:不遍历字符串键或其他类型的键
  • 性能略高:对连续数字索引的遍历比 pairs 稍快
1
2
3
for index, value in ipairs(table) do
-- 循环体
end

示例:

1
2
3
4
local fruits = { "apple", "banana", "cherry" }
for i, fruit in ipairs(fruits) do
print(i, fruit) -- 输出:1 apple, 2 banana, 3 cherry
end

注意:

如果 table 有空洞(如 { "a", nil, "c" }ipairs 会在第二个元素停止遍历

pairs

特点:

  • 全遍历:遍历 table 的 所有键值对(包括数字、字符串等任意类型的键)
  • 无序性:遍历顺序不确定(Lua 不保证顺序)
  • 无终止条件:即使有 nil 也会继续遍历
  • 更通用:适用于所有类型的 table

语法:

1
2
3
for key, value in pairs(table) do
-- 循环体
end

适用场景:

  • 遍历 非连续索引的 table(如 { "a", [3]="c", key="value" }
  • 需要访问所有键值对的场景

示例:

1
2
3
4
local data = { "a", [3] = "c", name = "Lua", version = 5.4 }
for k, v in pairs(data) do
print(k, v) -- 可能输出:1 a, 3 c, name Lua, version 5.4(顺序不确定)
end

循环控制语句

break

1
2
3
4
5
6
7
-- 跳出当前循环
for i = 1, 100 do
if i > 5 then
break -- 当i=6时退出循环
end
print(i)
end

return (在循环中使用)

1
2
3
4
5
6
7
8
function find(tbl, value)
for i, v in ipairs(tbl) do
if v == value then
return i -- 直接返回函数结果并终止循环
end
end
return nil
end

特殊用法与技巧

多条件组合

1
2
3
4
5
6
7
8
9
-- and/or组合
if (score > 90) and (attendance >= 0.8) then
print("优秀学生")
end

-- 范围判断
if 10 <= x and x <= 20 then
print("x在10到20之间")
end

模拟switch-case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
local action = "save"

-- 方法1: if-elseif链
if action == "new" then
createNew()
elseif action == "save" then
saveFile()
elseif action == "load" then
loadFile()
else
defaultAction()
end

-- 方法2: 使用表映射
local actions = {
new = createNew,
save = saveFile,
load = loadFile
}
(actions[action] or defaultAction)()

循环性能优化

1
2
3
4
5
6
7
8
9
10
11
12
-- 缓存表长度(避免重复计算)
local tbl = {1,2,3,4,5}
local len = #tbl
for i = 1, len do -- 比直接写#tbl效率更高
-- 处理元素
end

-- 缓存函数(避免重复查找)
local print = print -- 在频繁循环中提升性能
for i = 1, 1000 do
print(i)
end

常见错误与陷阱

浮点数循环

1
2
3
4
-- 可能因浮点精度导致意外结果
for x = 0, 1, 0.1 do
print(x) -- 可能不会精确到1.0
end

修改迭代表

1
2
3
4
local t = {1,2,3}
for i,v in ipairs(t) do
table.insert(t, v*2) -- 会导致无限循环!
end

误用break

1
2
3
4
5
6
7
8
-- break只能跳出最内层循环
while true do
for i=1,10 do
if condition then
break -- 只退出for循环,while循环继续
end
end
end

nil值处理

1
2
3
4
5
local t = {1,nil,3}
print(#t) -- 可能是1或3(取决于nil位置)
for i=1,#t do -- 可能漏掉元素
print(t[i])
end

函数

定义与调用

1
2
3
4
5
6
7
8
9
10
11
12
-- 基本函数定义
function greet(name)
return "Hello, " .. name
end

-- 等价写法
local greet = function(name)
return "Hello, " .. name
end

-- 函数调用
print(greet("Alice")) --> Hello, Alice

参数

传递规则

  • 参数数量灵活:可接受多于或少于声明参数的数量
  • 不足补nil,多余忽略
  • 支持可变参数(vararg)
1
2
3
4
5
6
function test(a, b)
print(a, b)
end

test(1) --> 1 nil
test(1, 2, 3) --> 1 2 (3被忽略)

可变参数(…)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function sum(...)
local total = 0
local args = {...} -- 转为表
for i, v in ipairs(args) do
total = total + v
end
return total
end

print(sum(1, 2, 3)) --> 6

-- 更高效的实现
function sum(...)
local total = 0
for i = 1, select("#", ...) do
total = total + select(i, ...)
end
return total
end

多返回值

1
2
3
4
5
6
7
8
9
function getDimensions()
return 1024, 768 -- 返回多个值
end

local w, h = getDimensions()
print(w, h) --> 1024 768

-- 只取部分返回值
local width = getDimensions() -- 只获取第一个返回值

闭包

1
2
3
4
5
6
7
8
9
10
11
function makeCounter()
local count = 0
return function() -- 匿名函数
count = count + 1
return count
end
end

local c1 = makeCounter()
print(c1()) --> 1
print(c1()) --> 2

函数作为参数

1
2
3
4
5
6
7
8
9
10
11
12
-- 高阶函数示例
function map(tbl, fn)
local result = {}
for i, v in ipairs(tbl) do
result[i] = fn(v)
end
return result
end

local numbers = {1, 2, 3}
local squared = map(numbers, function(x) return x*x end)
print(table.concat(squared, ",")) --> 1,4,9

尾调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 尾调用形式(不消耗栈空间)
function foo(n)
if n <= 0 then return end
print(n)
return foo(n-1) -- 尾调用
end

-- 非尾调用示例(会消耗栈空间)
function bar(n)
if n <= 0 then return end
print(n)
bar(n-1) -- 不是尾调用(隐含return nil)
end

-- 无限循环但不会栈溢出
function endless()
print("Running...")
return endless() -- 尾调用
end

表(Table)是 Lua 中唯一的数据结构,却可以模拟数组、字典、对象等多种数据结构

创建表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 空表
local t1 = {}

-- 数组式初始化(索引自动从1开始)
local colors = {"红", "绿", "蓝"}

-- 字典式初始化
local person = {
name = "Alice",
age = 25,
profession = "程序员"
}

-- 混合初始化
local mixed = {
"元素1", -- 自动索引1
"元素2", -- 自动索引2
key1 = "值1",
key2 = "值2"
}

访问元素:

1
2
3
4
5
6
7
8
9
10
-- 数组式访问
print(colors[1]) --> "红"

-- 字典式访问
print(person["name"]) --> "Alice"
print(person.age) --> 25 (语法糖)

-- 动态键名
local key = "age"
print(person[key]) --> 25

作为数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
local arr = {"a", "b", "c"}

-- 获取长度
print(#arr) --> 3

-- 遍历数组
for i = 1, #arr do
print(arr[i])
end

-- 使用ipairs遍历
for index, value in ipairs(arr) do
print(index, value)
end

⚠️ 注意:Lua数组索引从1开始,且遇到nil会中断长度计算

作为字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
local dict = {
name = "Alice",
age = 25,
[1] = "数字键1", -- 数字键和字符串键是不同的
["1"] = "字符串键1"
}

-- 遍历字典
for key, value in pairs(dict) do
print(key, value)
end

-- 检查键是否存在
if dict.name then
print("name存在")
end

元表(Metatable)与元方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
local t = {}
local mt = {
__index = function(table, key) -- 定义当访问不存在的键时的行为
return "默认值"
end,
__newindex = function(table, key, value) -- 定义新增键时的行为
rawset(table, key, value.."已修改")
end
}
setmetatable(t, mt)

print(t.a) --> "默认值"
t.b = "测试"
print(t.b) --> "测试已修改"

常用元方法:

元方法 触发时机 示例
__index 访问不存在的键 t.key
__newindex 给不存在的键赋值 t.key = value
__add + 运算 t1 + t2
__call 表作为函数调用 t()
__tostring 转换为字符串 tostring(t)

操作函数

函数 描述 示例
table.insert(t, [pos,] value) 插入元素 table.insert(t, 1, "x")
table.remove(t, [pos]) 删除元素 table.remove(t, 1)
table.concat(t, [sep]) 连接数组元素 table.concat(t, ",")
table.sort(t, [comp]) 排序 table.sort(t, function(a,b) return a>b end)
table.unpack(t) 返回数组所有元素 a,b,c = table.unpack(t)

实用代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-- 深拷贝表
function deepCopy(orig)
local copy = {}
for k, v in pairs(orig) do
if type(v) == "table" then
v = deepCopy(v)
end
copy[k] = v
end
return copy
end

-- 合并两个表
function merge(t1, t2)
local res = {}
for k, v in pairs(t1) do res[k] = v end
for k, v in pairs(t2) do res[k] = v end
return res
end

-- 表转字符串(便于调试)
function tableToString(t)
local result = {}
for k, v in pairs(t) do
table.insert(result, tostring(k).."="..tostring(v))
end
return "{"..table.concat(result, ", ").."}"
end

面向对象

Lua 本身没有内置的面向对象系统,但通过表和元表可以灵活实现各种 OOP 模式

原型模式(最常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 类定义
local Person = {name = "unnamed", age = 0} -- 原型对象

-- 构造函数
function Person:new(name, age)
local obj = {
name = name or self.name,
age = age or self.age
}
setmetatable(obj, {__index = self}) -- 设置原型
return obj
end

-- 方法定义
function Person:introduce()
print("我是"..self.name..",今年"..self.age.."岁")
end

-- 使用示例
local p1 = Person:new("Alice", 25)
p1:introduce() --> 我是Alice,今年25岁

继承实现

单继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-- 基类
local Animal = {
name = "未知生物",
sound = "..."
}

function Animal:new(name)
local obj = {name = name or self.name}
setmetatable(obj, {__index = self})
return obj
end

function Animal:makeSound()
print(self.name.."发出声音:"..self.sound)
end

-- 派生类
local Cat = Animal:new() -- 先创建基类实例
Cat.sound = "喵喵" -- 重写属性

-- 重写方法
function Cat:makeSound()
print(self.name.."优雅地叫:"..self.sound)
end

-- 使用
local cat = Cat:new("小花")
cat:makeSound() --> 小花优雅地叫:喵喵

多继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
local function multiInherit(...)
local classes = {...}
local obj = {}

-- 设置多个__index
setmetatable(obj, {
__index = function(table, key)
for _, class in ipairs(classes) do
local value = class[key]
if value then return value end
end
end
})

return obj
end

-- 飞行能力类
local Flyable = {
altitude = 0
}
function Flyable:fly()
self.altitude = self.altitude + 100
print(self.name.."飞到"..self.altitude.."米高空")
end

-- 创建同时继承Animal和Flyable的类
local Bird = Animal:new()
Bird = multiInherit(Bird, Flyable)

local bird = Bird:new("信天翁")
bird:makeSound() --> 信天翁发出声音:...
bird:fly() --> 信天翁飞到100米高空

私有成员实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
local function createClass()
-- 私有数据
local privateData = {}

-- 类定义
local MyClass = {}

function MyClass:new(name)
local obj = {name = name}
privateData[obj] = {
secret = math.random(1000) -- 私有属性
}

setmetatable(obj, {__index = self})

-- 在闭包中访问私有数据
function obj:getSecret()
return privateData[self].secret
end

return obj
end

return MyClass
end

local SecretClass = createClass()
local obj = SecretClass:new("test")
print(obj:getSecret()) --> 随机数
print(obj.secret) --> nil (无法直接访问)