【语法算法】
元方法 __lt 的触发就这一行:
setmetatable(t, { __lt = function(a, b) return a.score < b.score end })
两个表做 < 比较时——Lua 调用 __lt——排序比较的全部机制就这一行。可这一行的前后各有一个细节:前面是"两个操作数必须类型相同",后面是"table.sort 内部会反复调用"。今天拆开。
一、触发、排序、反复三段
__lt 的三段。触发:两个表做 < 比较时——Lua 查左操作数的元表有没有 __lt——没有再查右操作数的——查到了就调用并返回布尔值。排序:table.sort(t) 内部反复调用 < 比较相邻元素——如果表中的元素是带 __lt 的表,排序就按自定义规则走——排序的稳定性取决于比较函数是否严格弱序。反复:table.sort 的比较次数是 O(n log n)——__lt 会被反复调用——每次调用都要分配栈帧——__lt 函数里不要做复杂操作。三段连起来:__lt 玩的是"把排序规则从内置类型扩展到自定义类型"——表的排序从此可以按任何自定义规则进行。
graph TD
A["table.sort(t)"] --> B["比较 t[i] < t[j]"]
B --> C{"t[i]有__lt?"}
C -->|是| D["调用__lt(t[i], t[j])"]
D --> E["返回true: 交换"]
D --> F["返回false: 不交换"]
C -->|否| G["报错: attempt to compare"]
二、排序的正确写法
local Skill = {}
Skill.__index = Skill
function Skill.new(name, cd, priority)
return setmetatable({ name = name, cd = cd, priority = priority }, Skill)
end
Skill.__lt = function(a, b)
return a.priority < b.priority
end
local skills = {
Skill.new("火球", 3, 2),
Skill.new("冰箭", 2, 1),
Skill.new("雷电", 5, 3),
}
table.sort(skills)
for _, s in ipairs(skills) do
print(s.name, s.priority)
end
-- 冰箭 1
-- 火球 2
-- 雷电 3
table.sort(skills) 自动走 __lt——排序结果按 priority 升序——不需要传比较函数——排序规则封装在元方法里。
三、严格弱序与比较的纪律
严格弱序是排序的数学要求:如果 a < b 且 b < c,则 a < c——不满足严格弱序的比较函数会导致 table.sort 报错 "invalid order function"——这是排序最常见的坑。避免的方法是比较函数只看一个字段——如果需要多字段排序,先按第一字段比较,相等再按第二字段——不要在比较函数里做复杂的逻辑判断。最后一条纪律:__lt 只处理 < 操作——> 和 >= 会自动用 < 取反——但 == 走的是 __eq 不是 __lt——三个比较元方法各管各的,不能混用。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【游戏功能】 再抛一个老问题:奶量是固定好还是浮动好?固定回五十的术式,满血喝是五十、残血喝也是五十,账目清楚,可总差点意思…
【游戏功能】 前阵子一场"永动机事故"在群里传了半天:某个会喷火的精英怪被玩家围了一整晚,火喷了八个钟头没停——后台一看,它…
【游戏功能】 mode=mode==='red'?'blue':'red' ——先拆这一行。一个变量存着当前纹色,三元运算符…
【游戏功能】 上一版埋了个查了半宿的隐蔽报错:测试组报"玩家血条归零了人还站着",后台的死亡日志一条没有,复活接口也没被调过…
【游戏功能】 先抛一个坑:增益为什么只能自己挂?怒气、护体、神行,攻略里全在教你"什么时候开自己的招",可怪身上明晃晃挂着的…
【游戏功能】 昨天夜里一场线上事故把"反纹"顶上了台面:一个玩家把反纹挂在自己身上站桩挂机,怪的火球过来他没倒,反倒躺下的是…