Lua 从入门到精通 / 02 - 语法基础 / Syntax Basics
语法基础 / Syntax Basics
Lua 的语法设计哲学是"提供机制而非策略"——尽量少的语法糖,用基础机制组合出高级用法。
Lua’s syntax philosophy is “mechanisms, not policies” — minimal syntax sugar, composing advanced behavior from basic mechanisms.
🟢 基础 / Basics — 写第一行 Lua 代码
1. 变量与作用域 / Variables & Scope
Lua 中有两种变量声明方式:全局变量 和 局部变量。
-- 全局变量(默认)/ Global variable (default)
x = 10 -- 不需要声明,直接赋值
name = "Lua"
-- 局部变量 / Local variable(推荐!)
local y = 20 -- 用 local 关键字声明
local age = 30
为什么推荐 local? / Why prefer local?
-- 全局变量的问题 / Problems with globals:
function foo()
count = 1 -- 一不小心就污染了全局作用域
end
function bar()
print(count) -- 依赖隐式的全局变量,容易出 bug
end
-- 局部变量的好处 / Benefits of locals:
function foo()
local count = 1 -- 限制在函数内部
end
-- 更快(后面高级部分会解释为什么)
-- 更安全(不会意外覆盖其他变量)
-- 更清晰(作用域一目了然)
作用域规则 / Scope Rules:
local x = 10 -- 外层 x
do
local x = 20 -- 内层 x,遮蔽外层
print(x) -- 20
end
print(x) -- 10(内层 x 已销毁)
-- 变量的作用域从声明处开始,到所在代码块结束
-- A variable's scope starts at declaration, ends at block end
2. 基本数据类型 / Basic Data Types
Lua 有 8 种数据类型:
print(type(nil)) -- nil 空值
print(type(true)) -- boolean 布尔
print(type(42)) -- number 数字
print(type("hello")) -- string 字符串
print(type({})) -- table 表
print(type(print)) -- function 函数
print(type(coroutine.create(function() end))) -- thread 协程
print(type(io.stdin)) -- userdata 用户数据
快速示例 / Quick examples:
-- nil:表示"没有值"
local a = nil
print(a) -- nil
print(b) -- nil(未赋值的变量默认为 nil)
-- boolean:只有 true 和 false
local isActive = true
local isDone = false
-- number:所有数字都是浮点数(Lua 5.3+ 区分整数和浮点数)
local count = 42 -- 整数 / integer
local pi = 3.14159 -- 浮点数 / float
-- string:不可变的字节序列
local greeting = "Hello"
local name = 'Lua'
local multi = [[
多行字符串
Multi-line string
]]
-- table:Lua 唯一的数据结构容器
local arr = {1, 2, 3}
local dict = {name="Lua", version="5.4"}
-- function:一等公民
local add = function(a, b) return a + b end
3. 赋值 / Assignment
-- 普通赋值 / Normal assignment
local x = 10
local name = "Lua"
-- 多重赋值 / Multiple assignment
local a, b = 1, 2
print(a, b) -- 1 2
-- 交换变量(不需要临时变量)/ Swap without temp variable
a, b = b, a
print(a, b) -- 2 1
-- 赋值数量不匹配 / Mismatched counts
local x, y, z = 1, 2 -- z = nil
print(x, y, z) -- 1 2 nil
local a, b = 1, 2, 3 -- 3 被丢弃
print(a, b) -- 1 2
4. 块与语句 / Blocks & Statements
-- Lua 的语句分隔符是换行或分号(分号可选)
-- Statements separated by newline or semicolons (semicolons optional)
local x = 1 -- 分号可省略
local y = 2; -- 也可以加上
-- do...end 创建一个代码块
do
local temp = "I'm local to this block"
print(temp)
end
-- temp 在这里已经不存在了
-- 多行语句(Lua 会自动判断续行)
local result = 1
+ 2
+ 3 -- OK, Lua 知道上一行需要继续
print(result) -- 6
5. 运算符 / Operators
算术运算符 / Arithmetic:
print(10 + 3) -- 13 加法 / addition
print(10 - 3) -- 7 减法 / subtraction
print(10 * 3) -- 30 乘法 / multiplication
print(10 / 3) -- 3.3333 除法 / division
print(10 % 3) -- 1 取模 / modulo
print(10 ^ 2) -- 100 幂 / exponentiation
print(10 // 3) -- 3 整除 / floor division (Lua 5.3+)
print(-10) -- -10 取负 / unary negation
关系运算符 / Relational:
print(1 == 1) -- true 等于 / equal
print(1 ~= 2) -- true 不等于 / not equal(注意是 ~= 而不是 !=)
print(1 < 2) -- true 小于 / less than
print(1 > 2) -- false 大于 / greater than
print(1 <= 2) -- true 小于等于 / less or equal
print(1 >= 2) -- false 大于等于 / greater or equal
-- 类型不同也能比较(但要小心)
print(1 == "1") -- false(不同类型的值永远不相等)
print(1 == 1.0) -- true(Lua 5.3+:整数和浮点数可以相等)
逻辑运算符 / Logical:
-- and, or, not(注意:是英文单词,不是符号)
print(true and false) -- false
print(true or false) -- true
print(not true) -- false
-- 重要特性:and 和 or 返回操作数,而不是 true/false
-- Key feature: and/or return operands, not booleans
print(1 and "hello") -- "hello"(1 为真,返回第二个值)
print(nil and "hello") -- nil(nil 为假,直接返回 nil)
print(false or "hello") -- "hello"(false 为假,返回第二个值)
print(true or "hello") -- true(true 为真,直接返回 true)
-- 利用这个特性实现默认值 / Use this for default values
local name = user_name or "Guest" -- 如果 user_name 为 nil,使用 "Guest"
-- 三元运算符模拟(Lua 没有 ?: 语法)/ Simulating ternary operator
local status = (age >= 18) and "adult" or "minor"
-- 注意:这在 "adult" 为 false/nil 时不安全
字符串连接 / Concatenation:
local greeting = "Hello" .. " " .. "World"
print(greeting) -- Hello World
-- 注意:字符串连接会创建新字符串,大量连接时性能差
-- 用 table.concat 代替(后面会讲)
🟡 进阶 / Intermediate — 实用模式与陷阱
1. 类型判断与转换 / Type Checking & Coercion
-- type() 函数 / type() function
print(type(42)) -- "number"
print(type("hi")) -- "string"
print(type(true)) -- "boolean"
print(type(nil)) -- "nil"
print(type({})) -- "table"
print(type(print)) -- "function"
-- Lua 的隐式类型转换 / Implicit type coercion
print("10" + 1) -- 11(字符串自动转为数字)
print("hello" + 1) -- 错误!不能转换 / Error! cannot convert
-- 显式转换 / Explicit conversion
print(tonumber("123")) -- 123
print(tonumber("abc")) -- nil(转换失败返回 nil)
print(tostring(123)) -- "123"
print(tostring(nil)) -- "nil"
print(tostring(true)) -- "true"
-- 字符串拼接时自动转换 / Auto-concat in string contexts
print("The answer is " .. 42) -- "The answer is 42"
print("Value: " .. true) -- 错误!boolean 不会自动转字符串
print("Value: " .. tostring(true)) -- "Value: true"
2. 运算符优先级 / Operator Precedence
从高到低 / From highest to lowest:
-- 优先级表(从高到低)/ Precedence (highest to lowest):
-- ^
-- not # -(unary)
-- * / // %
-- + -
-- ..
-- < > <= >= ~= ==
-- and
-- or
-- 示例 / Examples:
print(2 + 3 * 4) -- 14(不是 20,* 优先于 +)
print((2 + 3) * 4) -- 20(括号改变优先级)
print(true or false and false) -- true(and 优先于 or)
print((true or false) and false) -- false
-- ^ 和 .. 是右结合 / ^ and .. are right-associative
print(2 ^ 3 ^ 2) -- 512(即 2^(3^2) = 2^9)
print("a" .. "b" .. "c") -- "abc"(右结合但结果相同)
3. 字符串字面量 / String Literals
-- 双引号和单引号(完全相同)/ Double and single quotes (identical)
local s1 = "hello"
local s2 = 'hello'
-- 转义序列 / Escape sequences
local s3 = "line1\nline2" -- 换行 / newline
local s4 = "tab\there" -- 制表符 / tab
local s5 = "backslash:\\" -- 反斜杠 / backslash
local s6 = "quote:\"" -- 双引号 / double quote
local s7 = "null:\0" -- 空字节 / null byte
local s8 = "unicode:\u{1F600}" -- Unicode(Lua 5.3+)/ Unicode emoji
-- 数字转义 / Numeric escape
local s9 = "\65\66\67" -- "ABC"(ASCII 码)
local s10 = "\x41\x42\x43" -- "ABC"(十六进制,Lua 5.2+)
-- 多行字符串 / Multi-line strings
local sql = [[
SELECT *
FROM users
WHERE age > 18
ORDER BY name
]]
-- 带缩进控制的多行字符串 / Multi-line with indent control
local code = [=[
function hello()
print("world")
end
]=]
4. Lua 没有 switch / No switch in Lua
Lua 没有 switch/case 语句,用 if-elseif 或表查找替代:
-- 方式一:if-elseif / Method 1: if-elseif
local function handleAction(action)
if action == "jump" then
print("Jumping!")
elseif action == "run" then
print("Running!")
elseif action == "idle" then
print("Standing still")
else
print("Unknown action")
end
end
-- 方式二:表查找(更快,更灵活)/ Method 2: table lookup (faster, more flexible)
local handlers = {
jump = function() print("Jumping!") end,
run = function() print("Running!") end,
idle = function() print("Standing still") end,
}
local function dispatch(action)
local handler = handlers[action]
if handler then
handler()
else
print("Unknown action: " .. action)
end
end
dispatch("jump") -- Jumping!
dispatch("fly") -- Unknown action: fly
🔴 高级 / Advanced — 语法背后的设计
1. 局部变量为什么更快? / Why Are Locals Faster?
-- 全局变量访问 / Global variable access:
print(x)
-- 字节码 / bytecode:
-- GETGLOBAL 0 0 ; _G["x"] ← 需要哈希表查找
-- 局部变量访问 / Local variable access:
local x = 10
print(x)
-- 字节码 / bytecode:
-- MOVE 0 0 ; R(0) ← 直接寄存器访问,O(1)
全局变量查找过程 / Global lookup process:
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌─────┐
│ GETGLOBAL│───►│ 环境表 │───►│ 哈希查找 │───►│ 值 │
│ "x" │ │ _G │ │ O(n)~O(1)│ │ 10 │
└─────────┘ └──────────┘ └──────────┘ └─────┘
局部变量查找过程 / Local lookup process:
┌─────────┐ ┌──────────┐
│ MOVE │───►│ 寄存器 0 │───► 值 = 10
│ R(0) │ │ O(1) │
└─────────┘ └──────────┘
2. 词法作用域与闭包预告 / Lexical Scope & Closure Preview
-- Lua 使用词法作用域(lexical scoping)
-- 内部函数可以访问外部函数的局部变量
function makeCounter()
local count = 0 -- 外部函数的局部变量
return function() -- 返回一个闭包
count = count + 1 -- 闭包"捕获"了 count
return count
end
end
local counter = makeCounter()
print(counter()) -- 1
print(counter()) -- 2
print(counter()) -- 3
-- 即使 makeCounter 已经返回,count 依然存在!
-- Even though makeCounter has returned, count still exists!
3. 语句结尾的歧义 / Statement Ending Ambiguity
-- Lua 的语句结束规则有时候会让人困惑
-- 问题 1:函数调用 vs 下一行的索引
local a = f
(x) -- Lua 会认为这是 f(x)!而不是两个语句
-- 问题 2:return 语句
function foo()
return -- 如果 return 后面有语句,Lua 可能会困惑
1 + 2 -- 这行可能不被当作 return 的值
end
-- 解决方案:return 后必须紧跟值,或者用括号
function bar()
return (1 + 2) -- 用括号明确意图
end
-- 分号可以消除歧义 / Semicolons resolve ambiguity
local a = f;
(x) -- 现在这是单独的语句了
4. ... 可变参数的字节码实现 / Variadic Args Bytecode
function test(...)
local args = {...}
print(#args)
end
-- 字节码 / bytecode:
-- 使用 VARARG 指令将可变参数复制到寄存器
-- VARARG 指令的实现会将栈上额外的参数打包成表
小结 / Summary
| 层级 | 你需要知道的 / What You Need to Know |
|---|---|
| 🟢 基础 | local 声明变量、5 种基本类型、算术/逻辑运算符、多重赋值 |
| 🟡 进阶 | 隐式转换陷阱、无 switch 用表替代、字符串转义、多行字符串 |
| 🔴 高级 | 局部变量比全局快的原因、词法作用域、语句歧义、分号的作用 |