强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 用表替代、字符串转义、多行字符串
🔴 高级局部变量比全局快的原因、词法作用域、语句歧义、分号的作用

下一章:数据类型 / Data Types