Lua 函数教程

Lua 函数

1. 课程信息

  • 课题:Lua 函数基础与进阶用法
  • 课时建议:60 ~ 90 分钟
  • 适合对象 :已掌握 Lua 变量、流程控制(if/for/while)与 table 基础的学习者

2. 教学目标

  • 理解函数的作用:封装逻辑、复用代码、提升可读性
  • 掌握定义与调用function ... end、匿名函数、局部/全局函数
  • 掌握参数与返回值 :多返回值、可变参数(...)、选择性接收
  • 理解 Lua 的"函数是一等公民":函数可赋值、可作为参数、可作为返回值
  • 能写出可维护的函数:明确输入输出、处理边界、合理命名

3. 知识要点

3.1 函数的定义与调用

Lua 中最常见的写法:

lua 复制代码
-- global function (not recommended in big projects)
function add(a, b)
    return a + b
end

print(add(2, 3)) -- 5

更推荐的局部函数:

lua 复制代码
local function add(a, b)
    return a + b
end

print(add(2, 3))
3.2 函数的本质:变量里装的值

下面两段代码等价:

lua 复制代码
local function add(a, b)
    return a + b
end
lua 复制代码
local add
add = function(a, b)
    return a + b
end

强调:函数就是一种值(和数字、字符串一样),因此可以赋值/传递。

3.3 参数与返回值
3.3.1 多参数、多返回值
lua 复制代码
local function divmod(a, b)
    return math.floor(a / b), a % b
end

local q, r = divmod(17, 5)
print(q, r) -- 3 2
3.3.2 只接收部分返回值
lua 复制代码
local function divmod(a, b)
    return math.floor(a / b), a % b
end

local q = divmod(17, 5)
print(q) -- 3 (only first return value)
3.3.3 返回值数量不固定的常见场景
  • string.find:找到了返回位置,没找到返回 nil
  • 自己写函数时也可以:失败返回 nil + 错误信息
lua 复制代码
local function toNumber(s)
    local n = tonumber(s)
    if n == nil then
        return nil, "not a number"
    end
    return n
end

local n, err = toNumber("12x")
print(n, err) -- nil  not a number
3.4 可变参数(Varargs):...
lua 复制代码
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)) -- 10

要点:

  • ... 表示所有传入的额外参数
  • select("#", ...) 获取可变参数数量
  • select(i, ...) 获取第 i 个参数
3.5 高阶函数:函数作为参数/返回值
3.5.1 函数作为参数(回调思想)
lua 复制代码
local function apply(a, b, op)
    return op(a, b)
end

local function mul(x, y)
    return x * y
end

print(apply(2, 3, mul)) -- 6
print(apply(2, 3, function(x, y) return x - y end)) -- -1
3.5.2 返回函数(工厂函数)
lua 复制代码
local function makeAdder(step)
    return function(x)
        return x + step
    end
end

local add10 = makeAdder(10)
print(add10(7)) -- 17
3.6 闭包(Closure)与作用域

闭包:函数"记住"它被创建时所在的外部变量。

lua 复制代码
local function makeCounter()
    local count = 0
    return function()
        count = count + 1
        return count
    end
end

local c1 = makeCounter()
print(c1()) -- 1
print(c1()) -- 2

local c2 = makeCounter()
print(c2()) -- 1 (independent)

要点:

  • count 是局部变量,但因为被内部函数引用,所以不会立刻释放
  • 适合实现计数器、缓存、封装私有状态
3.7 :. 的差异(面向对象常见)
lua 复制代码
local Player = {}
Player.__index = Player

function Player.new(name)
    return setmetatable({ name = name }, Player)
end

function Player.say(self, msg) -- dot style requires explicit self
    print(self.name .. ": " .. msg)
end

function Player:say2(msg) -- colon style passes self implicitly
    print(self.name .. ": " .. msg)
end

local p = Player.new("Alice")

p.say(p, "hello")
p:say2("hello")

规则:

  • obj:method(x) 等价于 obj.method(obj, x)
  • 定义时用 function T:method(...),调用时也用 : 保持一致

4. 课堂演示(建议流程)

  • Step 1(5min):用"重复代码"的例子引出函数封装
  • Step 2(10min) :讲 local function 与返回值
  • Step 3(15min) :多返回值、nil + err 的错误返回模式
  • Step 4(15min) :可变参数 ...select
  • Step 5(15min):高阶函数与闭包(重点)
  • Step 6(10min):. 的差异(结合 table + metatable 简单展示)

5. 练习题(课堂)

练习 1:写一个安全除法

实现 safeDiv(a, b)

  • b == 0 时返回 nil, "division by zero"
  • 否则返回 a / b
练习 2:实现 map

实现 map(arr, fn)

  • arr 是数组 table
  • fn 是函数:fn(value, index)
  • 返回一个新数组

示例:

  • 输入 {1,2,3}function(v) return v*2 end
  • 输出 {2,4,6}
练习 3:实现 filter

实现 filter(arr, pred)

  • pred(value, index) 返回 true/false
  • 返回符合条件的新数组

6. 作业(课后)

作业 1:实现一个缓存闭包

实现 memoize(fn):返回一个新函数,能缓存 fn 的计算结果。

  • 只要求支持单个参数(key)即可
  • 缓存用 table 保存
作业 2:实现一个小型日志系统

实现 makeLogger(prefix)

  • 返回一个函数 log(msg)
  • 每次调用输出:prefix .. ": " .. msg
  • prefix 由闭包保存

7. 常见坑总结

  • 多返回值丢失:把函数返回值放在表达式中(如拼接、作为参数)时,常只保留第一个返回值
  • 全局污染 :缺少 local 容易污染全局命名空间
  • :. 混用 :定义用 : 却用 . 调用(或反过来)会导致 self 不正确
  • nil 判断不严谨 :失败返回时要同时返回 nil, err,调用方要判断 if not ok then ... end

8. 扩展阅读(建议)

  • Lua manual:Functions / Vararg / Closures
  • 学会用 pcall/xpcall 做异常捕获(进阶主题)
相关推荐
superman超哥1 分钟前
Rust 借用分割技巧:突破借用限制的精确访问
开发语言·后端·rust·编程语言·借用分割技巧·借用限制·精准访问
程序炼丹师1 分钟前
C++ 中的 std::tuple (元组)的使用
开发语言·c++
程序员佳佳6 分钟前
【万字硬核】从GPT-5.2到Sora2:深度解构多模态大模型的“物理直觉”与Python全栈落地指南(内含Banana2实测)
开发语言·python·gpt·chatgpt·ai作画·aigc·api
小江村儿的文杰7 分钟前
UE4 PSO介绍一:PSO的定义(编辑中)
ue4·pso
不绝19113 分钟前
C#进阶——内存
开发语言·c#
风送雨13 分钟前
Go 语言进阶学习:第 1 周 —— 并发编程深度掌握
开发语言·学习·golang
小北方城市网15 分钟前
第 5 课:服务网格(Istio)实战|大规模微服务的流量与安全治理体系
大数据·开发语言·人工智能·python·安全·微服务·istio
jghhh0116 分钟前
自适应信号时频处理方法MATLAB实现(适用于非线性非平稳信号)
开发语言·算法·matlab
AC赳赳老秦16 分钟前
Go语言微服务文档自动化生成:基于DeepSeek的智能解析实践
大数据·开发语言·人工智能·微服务·golang·自动化·deepseek
古城小栈16 分钟前
Rust 之 迭代器
开发语言·rust