函数(基础篇)

7.1 函数定义

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 具名函数
function add(a, b)
return a + b
end

-- 匿名函数赋值给变量
local subtract = function(a, b)
return a - b
end

-- 调用
print(add(3, 4)) --> 7
print(subtract(10, 3)) --> 7

语法糖

当函数存储在表中时,可以使用冒号语法:

1
2
3
4
5
6
7
8
9
10
11
12
-- 普通方式
local obj = {}
obj.greet = function(self, name)
print("Hello, " .. name .. "!")
end
obj.greet(obj, "Lua") --> Hello, Lua!

-- 冒号语法糖(自动传递 self)
function obj:greet(name)
print("Hello, " .. name .. "!")
end
obj:greet("Lua") --> Hello, Lua!

7.2 参数传递

形参与实参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function print_info(name, age, city)
print("姓名:" .. name)
print("年龄:" .. age)
print("城市:" .. city)
end

-- 参数一一对应
print_info("张三", 25, "北京")

-- 参数不足:缺失的参数为 nil
print_info("李四", 30)
-- 城市:nil

-- 参数过多:多余的参数被忽略
print_info("王五", 28, "上海", "额外参数") -- 多余参数被忽略

参数默认值

Lua 不支持默认参数,但可以用 or 运算符实现:

1
2
3
4
5
6
7
8
9
function greet(name, greeting)
name = name or "访客"
greeting = greeting or "你好"
print(greeting .. "," .. name)
end

greet() --> 你好,访客
greet("张三") --> 你好,张三
greet("李四", "Hello") --> Hello,李四

参数类型检查

1
2
3
4
5
6
function divide(a, b)
assert(type(a) == "number", "a 必须是数字")
assert(type(b) == "number", "b 必须是数字")
assert(b ~= 0, "除数不能为 0")
return a / b
end

7.3 返回值

单一返回值

1
2
3
4
5
6
function square(n)
return n * n
end

local result = square(5)
print(result) --> 25

多返回值

这是 Lua 函数的一个重要特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 返回多个值
function min_max(a, b, c)
local min = math.min(a, b, c)
local max = math.max(a, b, c)
return min, max
end

local min_val, max_val = min_max(3, 7, 1)
print(min_val, max_val) --> 1 7

-- 忽略部分返回值
local _, max_val = min_max(3, 7, 1)
print(max_val) --> 7

-- 在表达式中,多返回值只取第一个
local result = min_max(3, 7, 1)
print(result) --> 1(只取了第一个返回值)

table.unpack 与多返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
local function get_user()
return "张三", 25, "北京"
end

-- 收集返回值到数组
local info = {get_user()}
print(info[1], info[2], info[3]) --> 张三 25 北京

-- 解包数组为返回值
local function combine(...)
return ...
end

local data = {"a", "b", "c"}
print(combine(table.unpack(data))) --> a b c

7.4 变长参数 ...

变长参数允许函数接受任意数量的参数:

基本用法

1
2
3
4
5
6
7
8
9
10
11
function sum(...)
local total = 0
for i = 1, select("#", ...) do
total = total + select(i, ...)
end
return total
end

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

{...} 收集参数

1
2
3
4
5
6
7
8
9
10
11
12
function print_all(...)
local args = {...}
for i, v in ipairs(args) do
print(i, v)
end
end

print_all("a", "b", "c")
-- 输出:
-- 1 a
-- 2 b
-- 3 c

使用 select()

select() 用于访问变长参数而无需创建表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function sum(...)
local total = 0
-- select("#", ...) 返回参数个数
for i = 1, select("#", ...) do
-- select(i, ...) 返回第 i 个参数
total = total + select(i, ...)
end
return total
end

-- select(n, ...) 返回从第 n 个开始到末尾的所有参数
local first, second = select(1, "a", "b", "c")
print(first, second) --> a b

local remaining = select(3, "a", "b", "c")
print(remaining) --> c

混合参数

普通参数在前,变长参数在后:

1
2
3
4
5
6
7
8
function printf(fmt, ...)
local args = {...}
-- 宏 printf 风格的简易实现
print(string.format(fmt, table.unpack(args)))
end

printf("姓名:%s,年龄:%d", "张三", 25)
--> 姓名:张三,年龄:25

7.5 具名参数

Lua 不支持 Python 风格的命名参数,但可以用 table 模拟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 用 table 传参,实现"具名参数"效果
function create_user(params)
local name = params.name or "匿名"
local age = params.age or 18
local city = params.city or "未知"
local email = params.email or ""

return {
name = name,
age = age,
city = city,
email = email
}
end

-- 调用时可以省略任意参数
local user1 = create_user({name = "张三", age = 25})
local user2 = create_user({name = "李四", email = "li@example.com"})

-- 更简洁的写法:去掉括号(当参数是唯一实参且是 table 或 string 时)
local user3 = create_user{name = "王五", city = "深圳"}

7.6 函数是第一类值

函数可以像其他值一样被赋值、传递、存储:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
-- 赋值给变量
local func = function(x) return x * 2 end
print(func(5)) --> 10

-- 存储在表中
local operations = {
add = function(a, b) return a + b end,
sub = function(a, b) return a - b end,
mul = function(a, b) return a * b end,
}

print(operations.add(3, 4)) --> 7
print(operations.mul(3, 4)) --> 12

-- 作为参数传递(回调函数)
function apply_to_array(arr, func)
local result = {}
for i, v in ipairs(arr) do
result[i] = func(v)
end
return result
end

local numbers = {1, 2, 3, 4, 5}
local doubled = apply_to_array(numbers, function(x) return x * 2 end)
-- doubled = {2, 4, 6, 8, 10}

-- 作为返回值
function create_multiplier(factor)
return function(x)
return x * factor
end
end

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

7.7 常用函数模式

回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function fetch_data(callback)
print("开始获取数据...")

-- 模拟异步操作
local data = {id = 1, name = "测试数据"}

if callback then
callback(data)
end
end

fetch_data(function(data)
print("收到数据:" .. data.name)
end)

排序比较器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
local people = {
{name = "张三", age = 25},
{name = "李四", age = 30},
{name = "王五", age = 20},
}

-- 按年龄升序
table.sort(people, function(a, b)
return a.age < b.age
end)

-- 按名字降序
table.sort(people, function(a, b)
return a.name > b.name
end)

工厂函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function create_counter(start, step)
start = start or 0
step = step or 1
local count = start

return {
next = function()
local current = count
count = count + step
return current
end,
reset = function()
count = start
end,
value = function()
return count
end
}
end

local counter = create_counter(10, 2)
print(counter.next()) --> 10
print(counter.next()) --> 12
print(counter.next()) --> 14
counter.reset()
print(counter.next()) --> 10

7.8 本章小结

  • 函数用 function 定义,可以具名或匿名
  • 参数不足时为 nil,多余参数被忽略
  • 支持多返回值,用逗号分隔
  • 变长参数 ... 配合 {...}select() 使用
  • 可以用 table 模拟具名参数
  • 函数是第一类值,可作为参数和返回值

练习题

  1. 实现一个 range(start, end, step) 函数,返回一个包含指定范围内所有数字的数组
  2. 实现 max(...) 函数,返回任意数量数字中的最大值
  3. 用 table 参数实现一个配置化函数 create_window({title, width, height, resizable})
  4. 写一个 compose(f, g) 函数,返回两个函数的复合函数 f(g(x))
  5. 实现一个 partial(func, ...) 函数,支持函数柯里化(部分应用)
ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
Lua基础教程 系列
第 7/15 篇
分享: