在 Lua 中,函數是一等公民(First-Class Citizen),這意味著函數可以像其他值一樣被賦值、傳遞和操作。以下是 Lua 函數定義的完整指南,涵蓋基礎語法、高級特性、設計模式及性能優化。
在Lua 中,函數定義的完整指南—目錄
- 一、基礎語法
- 1. 函數聲明
- 2. 匿名函數
- 二、核心特性
- 1. 多返回值
- 2. 變參函數(Variadic Function)
- 3. 函數作為返回值
- 三、進階特性
- 1. 閉包(Closure)
- 2. 尾調用優化(Tail Call Optimization)
- 3. 高階函數(Higher-Order Function)
- 四、函數屬性與元編程
- 1. 函數屬性
- 2. 元表與函數鉤子
- 五、性能優化指南
- 1. 避免閉包濫用
- 2. 內聯小函數
- 3. 使用局部函數
- 六、設計模式與最佳實踐
- 1. 策略模式
- 2. 工廠模式
- 3. 觀察者模式
- 七、調試與元信息
- 1. 獲取函數信息
- 2. 函數重載(模擬)
- 八、常見問題與解決方案
- 問題1:函數參數過多時的處理
- 問題2:遞歸深度過大導致棧溢出
一、基礎語法
1. 函數聲明
-- 基礎語法
function add(a, b)return a + b
end-- 調用
print(add(3, 5)) -- 輸出 8
2. 匿名函數
-- 無名稱的函數(常用于回調)
local function factorial(n)return n == 0 and 1 or n * factorial(n-1)
end-- 將函數賦值給變量
local square = function(x) return x * x end
print(square(4)) -- 輸出 16
二、核心特性
1. 多返回值
function divide_and_remainder(a, b)return math.floor(a / b), a % b
endlocal quotient, remainder = divide_and_remainder(10, 3)
print(quotient, remainder) -- 輸出 3 1
2. 變參函數(Variadic Function)
-- 使用 ... 接收任意數量參數
function sum(...)local total = 0for _, v in ipairs({...}) dototal = total + vendreturn total
endprint(sum(1,2,3,4)) -- 輸出 10
3. 函數作為返回值
function make_adder(x)return function(y)return x + yend
endlocal add5 = make_adder(5)
print(add5(3)) -- 輸出 8
三、進階特性
1. 閉包(Closure)
閉包是函數與其詞法環境的組合,可捕獲外部變量:
function counter()local count = 0return function()count = count + 1return countend
endlocal c = counter()
print(c()) -- 1
print(c()) -- 2
2. 尾調用優化(Tail Call Optimization)
尾調用不會增加調用棧深度,防止棧溢出:
function factorial(n, acc)acc = acc or 1if n == 0 then return acc endreturn factorial(n-1, n*acc) -- 尾遞歸優化
endprint(factorial(5)) -- 輸出 120
3. 高階函數(Higher-Order Function)
函數作為參數或返回值:
-- map 函數
function map(t, fn)local result = {}for _, v in ipairs(t) dotable.insert(result, fn(v))endreturn result
endlocal numbers = {1,2,3,4}
local squares = map(numbers, function(x) return x*x end)
print(table.unpack(squares)) -- 輸出 1 4 9 16
四、函數屬性與元編程
1. 函數屬性
function greet(name)return "Hello, " .. name
endgreet.__call = function(self, ...)return self.name .. ", " .. ...
endsetmetatable(greet, { __call = function(f, name) return "Hi, "..name end })print(greet("Alice")) -- 輸出 Hi, Alice
2. 元表與函數鉤子
-- 統計函數調用次數
local call_counts = {}
local function hook(func)return function(...)call_counts[func] = (call_counts[func] or 0) + 1return func(...)end
endlocal function add(a, b)return a + b
endadd = hook(add)
add(1,2)
add(3,4)
print(call_counts[add]) -- 輸出 2
五、性能優化指南
1. 避免閉包濫用
-- 低效寫法:頻繁創建閉包
for i = 1, 1000 dolocal function fn() return i end -- 每次循環都創建新閉包
end-- 優化寫法:使用局部變量捕獲
for i = 1, 1000 dolocal i_local = ilocal function fn() return i_local end
end
2. 內聯小函數
-- 頻繁調用的簡單函數可內聯
-- 優化前
local function square(x) return x*x end
for i = 1, 1e6 dosquare(i)
end-- 優化后
for i = 1, 1e6 dolocal x = i*i -- 直接展開計算
end
3. 使用局部函數
-- 全局函數查找較慢
function global_fn() end-- 優化:優先使用局部函數
local local_fn = function() end
六、設計模式與最佳實踐
1. 策略模式
local strategies = {add = function(a,b) return a+b end,multiply = function(a,b) return a*b end
}function calculate(operation, a, b)return (strategies[operation] or strategies.add)(a, b)
endprint(calculate("multiply", 3,4)) -- 輸出 12
2. 工廠模式
local function create_user(name, age)return {name = name,age = age,greet = function(self)print("I'm", self.name)end}
endlocal alice = create_user("Alice", 30)
alice:greet() -- 輸出 I'm Alice
3. 觀察者模式
local function observable()local listeners = {}return setmetatable({}, {__newindex = function(t,k,v)rawset(t, k, v)for _, listener in ipairs(listeners) dolistener(k, v)endend,add_listener = function(t, listener)table.insert(listeners, listener)end})
endlocal obj = observable()
obj:add_listener(function(key, value)print("Property changed:", key, value)
end)
obj.x = 10 -- 觸發監聽器輸出
七、調試與元信息
1. 獲取函數信息
function example(a, b)return a + b
endprint(debug.getinfo(example).name) -- 輸出 "example"
print(debug.getupvalue(example, 1)) -- 查看閉包的上層變量(若有)
2. 函數重載(模擬)
local function overloaded(fn)local cache = {}return function(...)local key = table.concat({...}, "|")if not cache[key] thencache[key] = fn(...)endreturn cache[key]end
endlocal function process(a, b)return a + b
endlocal process_overloaded = overloaded(process)
print(process_overloaded(1,2)) -- 計算并緩存
print(process_overloaded(1,2)) -- 直接返回緩存結果
八、常見問題與解決方案
問題1:函數參數過多時的處理
-- 使用表傳遞可選參數
function config(options)local defaults = {width=800, height=600}for k, v in pairs(options) dodefaults[k] = vendreturn defaults
endlocal cfg = config({height=400})
print(cfg.width, cfg.height) -- 輸出 800 400
問題2:遞歸深度過大導致棧溢出
-- 尾遞歸優化版本
function fibonacci(n, a, b)a = a or 0b = b or 1if n == 0 then return a endif n == 1 then return b endreturn fibonacci(n-1, b, a+b)
endprint(fibonacci(1000)) -- 不會導致棧溢出
通過掌握這些函數特性與模式,你可以編寫出高效、靈活的 Lua 代碼。對于復雜場景,建議結合元編程和設計模式,同時注意性能優化細節。