强曰为道

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

05 - 函数 / Functions

函数 / Functions

Lua 的函数是一等公民(first-class citizen)——可以存在变量里、作为参数传递、作为返回值返回。这是 Lua 最强大的特性之一。

Lua functions are first-class citizens — stored in variables, passed as arguments, returned from other functions. This is one of Lua’s most powerful features.


🟢 基础 / Basics — 定义和调用函数

1. 函数定义的两种方式

-- 方式一:命名函数(语法糖)/ Named function (syntactic sugar)
local function greet(name)
    print("Hello, " .. name)
end

-- 等价于:
local greet
greet = function(name)
    print("Hello, " .. name)
end

-- 方式二:匿名函数赋值 / Anonymous function assigned to variable
local add = function(a, b)
    return a + b
end

greet("Lua")     -- Hello, Lua
print(add(3, 5)) -- 8

2. 参数 / Parameters

-- 参数不足时,缺失的为 nil
local function show(a, b, c)
    print(a, b, c)
end

show(1)          -- 1  nil  nil
show(1, 2)       -- 1  2    nil
show(1, 2, 3)    -- 1  2    3
show(1, 2, 3, 4) -- 1  2    3   (多余的参数被丢弃)

-- 多返回值 / Multiple return values
local function swap(a, b)
    return b, a
end

local x, y = swap(1, 2)
print(x, y)    -- 2  1

-- 只接收部分返回值
local x = swap(1, 2)    -- x = 2, 第二个返回值被丢弃

3. 冒号语法 / Colon Syntax

-- 冒号语法自动传递 self 参数
-- Colon syntax automatically passes self

local dog = {name = "Buddy"}

function dog:bark()    -- 等价于 dog.bark = function(self)
    print(self.name .. " says: Woof!")
end

dog:bark()             -- Buddy says: Woof!
-- 等价于 dog.bark(dog)

-- 也可以用点号,但需要手动传递 self
function dog.sit(self)
    print(self.name .. " is sitting")
end

dog:sit()    -- Buddy is sitting

🟡 进阶 / Intermediate — 高级函数特性

1. 可变参数 / Variadic Arguments

-- ... 表示可变参数
local function sum(...)
    local result = 0
    for _, v in ipairs({...}) do    -- 将 ... 转为表
        result = result + v
    end
    return result
end

print(sum(1, 2, 3, 4, 5))    -- 15

-- 更高效的方式:用 select()
local function sum2(...)
    local result = 0
    for i = 1, select("#", ...) do    -- select("#", ...) 返回参数个数
        result = result + select(i, ...)    -- select(i, ...) 返回第 i 个及之后的参数
    end
    return result
end

-- 将可变参数固定地赋给变量
local function first3(...)
    local a, b, c = ...
    return a, b, c
end

print(first3(1, 2, 3, 4, 5))    -- 1  2  3

2. 闭包 / Closures

-- 闭包 = 函数 + 它捕获的外部变量
-- Closure = function + captured external variables

function makeCounter(start)
    local count = start or 0    -- 被闭包捕获的"上值"
    return function()
        count = count + 1       -- 闭包修改了外部变量
        return count
    end
end

local c1 = makeCounter(0)
local c2 = makeCounter(100)

print(c1())    -- 1
print(c1())    -- 2
print(c1())    -- 3
print(c2())    -- 101
print(c2())    -- 102
-- c1 和 c2 拥有独立的 count 变量!

闭包的实用场景:

-- 场景一:函数工厂
function multiplier(factor)
    return function(x)
        return x * factor
    end
end

local double = multiplier(2)
local triple = multiplier(3)
print(double(5))    -- 10
print(triple(5))    -- 15

-- 场景二:私有变量(模块模式)
function makeBankAccount(initialBalance)
    local balance = initialBalance    -- 私有变量,外部无法直接访问
    
    return {
        deposit = function(amount)
            balance = balance + amount
        end,
        withdraw = function(amount)
            if amount > balance then
                error("Insufficient funds")
            end
            balance = balance - amount
        end,
        getBalance = function()
            return balance
        end,
    }
end

local account = makeBankAccount(1000)
account.deposit(500)
print(account.getBalance())    -- 1500
-- print(balance)             -- nil(无法直接访问)

-- 场景三:回调与延迟执行
function defer(fn, delay)
    return function(...)
        local args = {...}
        os.execute("sleep " .. delay)
        return fn(table.unpack(args))
    end
end

3. 闭包与变量绑定时机 / Closure Variable Binding

-- 陷阱:闭包捕获的是变量的引用,不是值
-- Trap: closures capture variable references, not values

local functions = {}
for i = 1, 5 do
    functions[i] = function() return i end
end
-- 所有函数共享同一个 i 变量!
for _, f in ipairs(functions) do
    print(f())    -- 全部输出 6!(循环结束后 i = 6)
end

-- 正确做法:用立即调用创建新的作用域
local functions2 = {}
for i = 1, 5 do
    functions2[i] = (function(j)    -- j 是新的局部变量
        return function() return j end
    end)(i)    -- 立即调用,传入当前的 i 值
end
for _, f in ipairs(functions2) do
    print(f())    -- 1, 2, 3, 4, 5
end

-- Lua 5.4 的解决方案:使用 <close> 或 to-be-closed 变量
-- (更常见的做法是在循环内用 do...end 块)
for i = 1, 5 do
    local j = i    -- 创建新的局部变量
    functions[i] = function() return j end
end

4. 尾调用优化 / Tail Call Optimization (TCO)

-- 尾调用:函数的最后一个动作是调用另一个函数
-- Tail call: the last action is calling another function

-- 有尾调用优化(不会栈溢出)
function factorial(n, acc)
    acc = acc or 1
    if n <= 1 then return acc end
    return factorial(n - 1, n * acc)    -- 尾调用位置!
end
print(factorial(100000))    -- 不会栈溢出

-- 没有尾调用优化(会栈溢出)
function badFactorial(n)
    if n <= 1 then return 1 end
    return n * badFactorial(n - 1)    -- 不是尾调用!需要在调用后做乘法
end
-- badFactorial(100000)    -- 栈溢出!

-- 尾调用的条件 / Conditions for TCO:
-- 1. 调用是函数的最后一个动作
-- 2. 调用的返回值直接被返回
-- 3. 不在保护模式(pcall/xpcall)中

-- ✅ 是尾调用
function f() return g() end
function f(x) if x then return g() else return h() end end

-- ❌ 不是尾调用
function f() return g() + 1 end         -- 还要做加法
function f() return (g()) end            -- 调整为单值
function f() g() end                     -- 没有返回

🔴 高级 / Advanced — 闭包与 Upvalue 的内部实现

1. 闭包的内部结构 / Closure Internals

Lua 闭包在内存中的结构:

┌───────────────────────────────────────┐
│           Closure (LClosure)          │
├───────────────────────────────────────┤
│  Proto*   ──────────► 函数原型/字节码   │
│  nupvalues: 2                         │
│  upvals[0] ──────► ┌──────────────┐   │
│                     │  Upvalue     │   │
│                     │  .value = 10 │   │
│                     │  .ref = 1    │   │
│                     │  .open = false│  │
│                     └──────────────┘   │
│  upvals[1] ──────► ┌──────────────┐   │
│                     │  Upvalue     │   │
│                     │  .value = "x"│   │
│                     └──────────────┘   │
└───────────────────────────────────────┘

Open Upvalue(未关闭):        Closed Upvalue(已关闭):
┌─────────────────────┐       ┌─────────────────────┐
│ Upvalue             │       │ Upvalue             │
│ .open = true        │       │ .open = false       │
│ .index ──► 栈上位置  │       │ .value = 实际的值    │
└─────────────────────┘       └─────────────────────┘

2. Upvalue 的共享 / Upvalue Sharing

-- 多个闭包可以共享同一个 upvalue
function makeGetSet()
    local value = 0    -- 一个 upvalue,被下面两个函数共享
    
    local function get() return value end
    local function set(v) value = v end
    
    return get, set
end

local get, set = makeGetSet()
set(42)
print(get())    -- 42
-- get 和 set 共享同一个 value upvalue
-- 修改通过 set 做的,get 能看到

3. select() 的内部实现原理

-- select(index, ...) 的特殊行为
-- select("#", ...) 返回可变参数个数
-- select(i, ...) 返回第 i 个及之后的所有参数

-- 内部实现非常巧妙:
-- select 不会创建新表,而是直接操作栈
-- 这就是为什么 select 比 {...} 更高效

-- 对比 / Comparison:
-- 方式一:{...} 创建新表,有内存开销
local function bad(...)
    local args = {...}    -- 分配新表
    for i = 1, #args do
        process(args[i])
    end
end

-- 方式二:select 零开销
local function good(...)
    for i = 1, select("#", ...) do
        process(select(i, ...))
    end
end

4. 函数调用的栈帧 / Function Call Stack Frame

┌─────────────────────────────────────────┐
│          Lua 调用栈 / Call Stack         │
│                                         │
│  ┌─────────────────────────────────┐    │
│  │  Frame 3: function g()          │    │
│  │    locals: z = 30               │    │
│  │    PC: 5                        │    │
│  ├─────────────────────────────────┤    │
│  │  Frame 2: function f()          │    │
│  │    locals: y = 20               │    │
│  │    PC: 8                        │    │
│  ├─────────────────────────────────┤    │
│  │  Frame 1: main chunk            │    │
│  │    locals: x = 10               │    │
│  │    PC: 3                        │    │
│  └─────────────────────────────────┘    │
└─────────────────────────────────────────┘

尾调用时(TCO),Frame 2 会被 Frame 3 替代:
┌─────────────────────────────────┐
│  Frame 3: function g()          │  ← Frame 2 被回收
│    PC: 1                        │
├─────────────────────────────────┤
│  Frame 1: main chunk            │
│    PC: 3                        │
└─────────────────────────────────┘

小结 / Summary

层级你需要知道的 / What You Need to Know
🟢 基础function 定义、return、多返回值、冒号语法、self
🟡 进阶可变参数(…)、select()、闭包、尾调用优化
🔴 高级Upvalue 内部结构、闭包共享变量、栈帧与 TCO 原理

下一章:表 / Tables