可变参数函数 #

一、可变参数基础 #

1.1 基本语法 #

lua
-- 使用 ... 表示可变参数
local function sum(...)
    local total = 0
    for _, v in ipairs({...}) do
        total = total + v
    end
    return total
end

print(sum(1, 2, 3, 4, 5))  -- 15
print(sum(10, 20))         -- 30
print(sum())               -- 0

1.2 固定参数与可变参数 #

lua
-- 固定参数在前,可变参数在后
local function format_string(separator, ...)
    local parts = {...}
    return table.concat(parts, separator)
end

print(format_string("-", "a", "b", "c"))  -- a-b-c
print(format_string(", ", 1, 2, 3))       -- 1, 2, 3

1.3 访问可变参数 #

lua
-- 方式1:打包成表
local function print_args(...)
    local args = {...}
    for i, v in ipairs(args) do
        print(i, v)
    end
end

print_args("a", "b", "c")
-- 1    a
-- 2    b
-- 3    c

-- 方式2:直接遍历
local function print_args2(...)
    for i = 1, select("#", ...) do
        print(i, select(i, ...))
    end
end

二、select 函数 #

2.1 select 基础 #

lua
-- select("#", ...) 返回参数数量
local function count_args(...)
    return select("#", ...)
end

print(count_args(1, 2, 3))  -- 3
print(count_args())         -- 0

-- select(n, ...) 返回第 n 个参数及之后的所有参数
local function test_select(...)
    print("从第2个开始:", select(2, ...))
    print("从第3个开始:", select(3, ...))
end

test_select("a", "b", "c", "d")
-- 从第2个开始:    b    c    d
-- 从第3个开始:    c    d

2.2 遍历可变参数 #

lua
-- 使用 select 遍历
local function sum(...)
    local total = 0
    for i = 1, select("#", ...) do
        local v = select(i, ...)
        total = total + v
    end
    return total
end

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

-- 处理 nil 参数
local function print_all(...)
    for i = 1, select("#", ...) do
        local v = select(i, ...)
        print(i, v)
    end
end

print_all("a", nil, "c")
-- 1    a
-- 2    nil
-- 3    c

2.3 获取特定参数 #

lua
-- 获取第一个参数
local function first(...)
    return ...
end

-- 获取最后一个参数
local function last(...)
    return select(select("#", ...), ...)
end

print(first(1, 2, 3))  -- 1
print(last(1, 2, 3))   -- 3

-- 获取第 n 个参数
local function nth(n, ...)
    return select(n, ...)
end

print(nth(2, "a", "b", "c"))  -- b

三、参数打包与解包 #

3.1 打包参数 #

lua
-- 打包成表
local function pack(...)
    return {...}
end

local args = pack(1, 2, 3, 4, 5)
print(args[1], args[3])  -- 1    3

-- 带计数的打包
local function pack_with_count(...)
    return {
        n = select("#", ...),
        ...
    }
end

local result = pack_with_count(1, nil, 3)
print(result.n)       -- 3
print(result[1])      -- 1
print(result[2])      -- nil
print(result[3])      -- 3

3.2 解包参数 #

lua
-- table.unpack 解包表
local arr = {1, 2, 3, 4, 5}
print(table.unpack(arr))  -- 1    2    3    4    5

-- 指定范围解包
print(table.unpack(arr, 2, 4))  -- 2    3    4

-- 解包作为函数参数
local function add(a, b, c)
    return a + b + c
end

local nums = {1, 2, 3}
print(add(table.unpack(nums)))  -- 6

3.3 转发参数 #

lua
-- 将参数转发给另一个函数
local function wrapper(func, ...)
    print("调用前")
    local result = func(...)
    print("调用后")
    return result
end

local function add(a, b)
    return a + b
end

print(wrapper(add, 1, 2))
-- 调用前
-- 调用后
-- 3

四、实用示例 #

4.1 可变参数求值 #

lua
-- 求最大值
local function max(...)
    local max_val = select(1, ...)
    for i = 2, select("#", ...) do
        local v = select(i, ...)
        if v > max_val then
            max_val = v
        end
    end
    return max_val
end

print(max(3, 1, 4, 1, 5, 9, 2, 6))  -- 9

-- 求最小值
local function min(...)
    local min_val = select(1, ...)
    for i = 2, select("#", ...) do
        local v = select(i, ...)
        if v < min_val then
            min_val = v
        end
    end
    return min_val
end

print(min(3, 1, 4, 1, 5, 9, 2, 6))  -- 1

4.2 可变参数格式化 #

lua
-- 类似 printf 的函数
local function printf(format, ...)
    io.write(string.format(format, ...))
end

printf("Hello, %s! You have %d messages.\n", "Lua", 5)
-- Hello, Lua! You have 5 messages.

-- 日志函数
local function log(level, ...)
    local timestamp = os.date("%Y-%m-%d %H:%M:%S")
    local message = string.format(...)
    print(string.format("[%s] [%s] %s", timestamp, level, message))
end

log("INFO", "User %s logged in", "Alice")
log("ERROR", "Failed to open file: %s", "data.txt")

4.3 可变参数构建 #

lua
-- 创建数组
local function arr(...)
    return {...}
end

local a = arr(1, 2, 3, 4, 5)
print(a[1], a[3])  -- 1    3

-- 创建字典
local function dict(...)
    local result = {}
    local args = {...}
    for i = 1, #args, 2 do
        result[args[i]] = args[i + 1]
    end
    return result
end

local d = dict("name", "Lua", "version", 5.4)
print(d.name, d.version)  -- Lua    5.4

4.4 链式调用 #

lua
-- 链式操作
local Chain = {}
Chain.__index = Chain

function Chain.new(...)
    return setmetatable({values = {...}}, Chain)
end

function Chain:map(func)
    local result = {}
    for i, v in ipairs(self.values) do
        result[i] = func(v, i)
    end
    self.values = result
    return self
end

function Chain:filter(func)
    local result = {}
    for _, v in ipairs(self.values) do
        if func(v) then
            table.insert(result, v)
        end
    end
    self.values = result
    return self
end

function Chain:reduce(func, initial)
    local acc = initial
    for _, v in ipairs(self.values) do
        acc = func(acc, v)
    end
    return acc
end

function Chain:result()
    return table.unpack(self.values)
end

-- 使用
local result = Chain.new(1, 2, 3, 4, 5)
    :map(function(x) return x * 2 end)
    :filter(function(x) return x > 4 end)
    :reduce(function(acc, v) return (acc or 0) + v end)

print(result)  -- 18 (6 + 8 + 10)

五、高级技巧 #

5.1 可变参数与命名参数 #

lua
-- 混合使用
local function create_element(tag, options, ...)
    options = options or {}
    local attrs = {}
    for k, v in pairs(options) do
        table.insert(attrs, string.format('%s="%s"', k, v))
    end
    
    local attr_str = #attrs > 0 and " " .. table.concat(attrs, " ") or ""
    local content = table.concat({...})
    
    return string.format("<%s%s>%s</%s>", tag, attr_str, content, tag)
end

print(create_element("div", {class = "container", id = "main"}, "Hello"))
-- <div class="container" id="main">Hello</div>

5.2 可变参数递归 #

lua
-- 递归处理可变参数
local function flatten(...)
    local result = {}
    
    local function process(...)
        for i = 1, select("#", ...) do
            local v = select(i, ...)
            if type(v) == "table" then
                process(table.unpack(v))
            else
                table.insert(result, v)
            end
        end
    end
    
    process(...)
    return result
end

local flat = flatten(1, {2, 3}, 4, {5, {6, 7}})
print(table.concat(flat, ", "))  -- 1, 2, 3, 4, 5, 6, 7

5.3 可变参数缓存 #

lua
-- 缓存可变参数函数的结果
local function memoize_varargs(func)
    local cache = {}
    return function(...)
        local key = table.concat({...}, "\0")
        if cache[key] == nil then
            cache[key] = {func(...)}
        end
        return table.unpack(cache[key])
    end
end

local fib = memoize_varargs(function(n)
    if n <= 1 then return n end
    return fib(n - 1) + fib(n - 2)
end)

print(fib(50))  -- 快速计算

六、常见陷阱 #

6.1 nil 参数处理 #

lua
-- 问题:nil 参数会被忽略
local function bad_count(...)
    return #{...}
end

print(bad_count(1, nil, 3))  -- 可能是 1 或 3

-- 解决:使用 select("#", ...)
local function good_count(...)
    return select("#", ...)
end

print(good_count(1, nil, 3))  -- 3

6.2 尾部 nil #

lua
-- 问题:尾部 nil 丢失
local function process(...)
    local args = {...}
    for i = 1, #args do  -- # 遇到 nil 会停止
        print(i, args[i])
    end
end

process(1, nil, 3, nil)  -- 可能只打印 1

-- 解决:保存参数数量
local function process2(...)
    local n = select("#", ...)
    local args = {...}
    for i = 1, n do
        print(i, args[i])
    end
end

6.3 性能考虑 #

lua
-- 避免重复打包
local function slow(...)
    for i = 1, 1000 do
        local args = {...}  -- 每次都创建新表
        process(args)
    end
end

-- 推荐:打包一次
local function fast(...)
    local args = {...}
    local n = select("#", ...)
    for i = 1, 1000 do
        process(args, n)
    end
end

七、总结 #

本章介绍了 Lua 的可变参数函数:

  1. 基本语法:使用 ... 表示可变参数
  2. select 函数:获取参数数量和特定参数
  3. 打包与解包{...}table.unpack
  4. 实用示例:求值、格式化、链式调用
  5. 高级技巧:递归处理、缓存
  6. 常见陷阱:nil 参数处理、尾部 nil

可变参数是 Lua 函数的重要特性。下一章,我们将学习表(Table)。

最后更新:2026-03-27