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 做异常捕获(进阶主题)
相关推荐
Scott9999HH8 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社9 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海9 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖10 小时前
JDK 26 新特性详解
java·开发语言
马优晨10 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区12 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大13 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai13 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜13 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa13 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio