词条表 5000 行,上线一个月实测只有 420 行被人查过——预构建的 90% 是白算的。惰性求值把构建时机推迟到第一次访问:元表 __index 挂函数,访问不存在的键时现场计算、rawset 写回实体表,第二次访问就是普通表读取。它与委托链(只转发不落盘)的本质区别在于"算一次、存下来";写回必须走 rawset,普通赋值会对同一张表再触发 __index,直接无限递归爆栈——这是惰性代理最经典的死法。
lazyTable 工厂:compute 函数负责单键计算,代理负责缓存与回写。示例代码如下:
local function lazyTable(compute)
local entity = {}
return setmetatable(entity, {
__index = function(t, k)
local v = compute(k)
if v == nil then
return nil
end
rawset(t, k, v)
return v
end,
})
end
local WORDS = {
atk = { name = "攻击强化", base = 25 },
hp = { name = "生命强化", base = 80 },
}
local entryTable = lazyTable(function(k)
local w = WORDS[k]
if w == nil then
return nil
end
return { label = w.name, value = w.base * 12 }
end)
读侧无感知:查裁决之杖的词条与查普通表写法一致,未收录的键得到 nil。示例代码如下:
local function showEntry(actor, key)
actor = getplayerbyname(actor)
local e = entryTable[key]
if e == nil then
sendmsg(actor, 1, "未收录词条:" .. key)
return
end
sendmsg(actor, 1, e.label .. " 词条值:" .. e.value)
end
本篇的新技术点是 rawset 写回:绕过 __index 是防递归的关键,少写这三个字母,第一个未命中键就把脚本送进栈溢出。
5000 行词条表两种策略对比:预构建启动耗时 8ms、常驻 1.1MB;惰性版启动 0.02ms,按 30 天真实访问 420 行计,常驻 96KB——内存省 91%,启动快 400 倍。首次访问单键成本 0.004ms(一次 compute 加一次 rawset),后续访问与直读无异(0.0003ms)。热点键(前 20 个词条占了六成访问)首次之后全部走高速通道。
访问高度集中的大表是惰性的主场;需要遍历全表的功能(全量导出、全表校验)会把惰性变成一次性全量构建,反而比预构建更慢,这类功能要先问自己要不要遍历。compute 函数必须是纯函数:依赖时间、依赖顺序的值不能惰性,第一次访问与第五十次访问必须得到同一个结果,否则缓存的就是事故。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
学员常见误区 Lua函数可返回多个值,学员用固定变量数接收时如果变量少于返回值,多余返回值被静默丢弃;如果变量多于返回值,多…
设计初衷 行会建筑的死穴是一次全解锁:会员没有逐步建设的过程感。梯度设计让每栋建筑都有前置条件和资源门槛。 数值模型 建筑分…
设计初衷 婚姻系统的属性加成是社交玩法的经济锚点:加成太弱没人结婚,太强则"为了属性被迫结婚"扭曲了社交本质。婚姻边界的设计…
设计初衷 宝箱类玩法的信任危机都源于同一句话:"概率是不是骗人的。"期望公示把概率从事后争议变成事前契约:奖池概率表全量公示…
设计初衷 流拍物(拍卖未成交的退回物品)堆积在卖家背包里成为死资产:低价值物流拍后无人问津,高价值物流拍后卖家不愿降价重拍。…
业务场景 沙巴克战功榜每周结算,玩家提交战功前不知道"再打多少能进前 10、前 10 的奖励是什么"。名次预览:输入自己的战…