Lua 教程 - 第四章:函数深入
第四章:函数深入
函数是 Lua 程序的重要组成部分。在 Lua 中,函数是第一类值(First-Class Value),这意味着函数可以存储在变量中,可以作为参数传递给其他函数,也可以作为返回值。
本章将深入讲解函数的定义、参数传递、多返回值、可变参数以及闭包等高级特性。
4.1 函数基础
函数定义
使用 function 关键字定义函数。
-- 定义一个计算阶乘的函数
function factorial(n)
if n == 0 then
return 1
else
return n * factorial(n - 1)
end
end
print(factorial(5)) -- 120
这实际上是以下形式的语法糖:
factorial = function(n)
-- ...
end
函数调用
调用函数时,如果有参数,必须加上圆括号 ()。哪怕没有参数,也必须加上 ()。
特例:如果函数只有一个参数,且该参数是字符串字面量或表构造器,则可以省略括号。
print "Hello" -- 等同于 print("Hello")
print {1, 2, 3} -- 等同于 print({1, 2, 3})
参数传递
Lua 的参数传递机制类似于赋值:实参的值被赋给形参。
-
多余的参数会被忽略。
-
缺少的参数会被初始化为
nil。
function f(a, b)
print(a, b)
end
f(1) -- 1 nil
f(1, 2) -- 1 2
f(1, 2, 3) -- 1 2
多返回值
Lua 函数非常独特的一点是支持返回多个值。只需在 return 语句后列出所有返回值即可。
function swap(x, y)
return y, x
end
a, b = swap(10, 20)
print(a, b) -- 20 10
如果函数调用不是表达式的最后一个元素,只返回第一个值。
print(swap(10, 20), "end") -- 20 end
print(swap(10, 20)) -- 20 10
变长参数
使用 ... 表示变长参数。在函数体内,... 表现为一个包含所有额外参数的表达式。
function sum(...)
local s = 0
for i, v in ipairs{...} do
s = s + v
end
return s
end
print(sum(1, 2, 3, 4)) -- 10
在 Lua 5.2+ 中,还可以使用 table.pack(...) 将参数打包成一个表,这对于包含 nil 的参数列表更安全(因为它包含一个 n 字段表示参数个数)。
4.2 高级函数特性
闭包 (Closure)
闭包是指一个函数加上该函数所需访问的非局部变量(Upvalue)。简单来说,闭包可以让函数访问并操作其定义时所在作用域的变量,即使该作用域已经结束。
function newCounter()
local count = 0
return function() -- 匿名函数
count = count + 1
return count
end
end
c1 = newCounter()
print(c1()) -- 1
print(c1()) -- 2
c2 = newCounter() -- 新的闭包,拥有独立的 count
print(c2()) -- 1
在上面的例子中,newCounter 返回了一个匿名函数。虽然 newCounter 执行完毕后局部变量 count 本该销毁,但因为返回的匿名函数引用了它,所以 count 会一直存在于闭包中。
尾调用 (Tail Call)
尾调用是指函数的最后一个动作是调用另一个函数。Lua 支持尾调用优化(Tail Call Optimization),这意味着尾调用不会消耗栈空间。这使得我们可以编写无限递归函数而不会导致栈溢出。
function foo(n)
if n > 0 then
return foo(n - 1) -- 尾调用
end
end
以下情况不是尾调用:
-
return foo(n) + 1(调用后还有加法操作) -
return (foo(n))(被括号包裹,强制返回一个值)
函数作为参数(高阶函数)
函数可以作为参数传递给另一个函数。例如 table.sort 就接受一个比较函数。
students = {
{name = "Alice", score = 90},
{name = "Bob", score = 85},
{name = "Charlie", score = 95}
}
table.sort(students, function(a, b)
return a.score > b.score -- 按分数降序排列
end)
for _, s in ipairs(students) do
print(s.name, s.score)
end
4.3 函数式编程初探
Lua 的灵活性使其非常适合函数式编程风格。
-
map: 对集合中的每个元素应用函数。
-
filter: 筛选符合条件的元素。
-
reduce: 将集合归约为单个值。
虽然 Lua 标准库没有直接提供这些函数,但我们可以轻松实现它们。
function map(func, t)
local new_t = {}
for i, v in ipairs(t) do
new_t[i] = func(v)
end
return new_t
end
t = {1, 2, 3}
t2 = map(function(x) return x * 2 end, t)
-- t2 is {2, 4, 6}
练习题
-
编写一个函数
average(...),计算任意数量参数的平均值。 -
编写一个函数
power(x, n),使用递归计算x的n次幂。 -
利用闭包实现一个简单的“状态机”,每次调用返回 “STATE_A”, “STATE_B”, “STATE_C” 循环切换。
-
重写 4.1 节的
sum函数,使其支持直接传入一个数组作为参数(提示:检查第一个参数类型)。
下一章预告:表(Table)是 Lua 最强大的数据结构,也是唯一的复合数据结构。下一章我们将深入研究表的操作、元表(Metatable)以及如何实现面向对象编程。

