迭代器是遍歷集合的核心抽象,Lua 通過閉包實現有狀態迭代器,通過泛型 for 的三值協議(迭代器函數、恆定狀態、控制變量)統一遍歷語義。本文從基礎模式進階到 ipairs/pairs 內部實現,揭示無狀態迭代器的高效原理。
function values(t)
local i = 0
return function() -- 閉包捕獲 i 和 t
i = i + 1
return t[i]
end
end
local testTb = {10, 20, 30}
for v in values(testTb) do
print(v) -- 10, 20, 30
end
每次調用返回的匿名函數保持對 i 的引用,形成「函數+狀態」的迭代器模式。
語法:for <var-list> in <exp-list> do <body> end
執行流程:
<exp-list>,必須返回三個值:迭代器函數、恆定狀態、控制變量初值。(恆定狀態, 控制變量) 調用迭代器函數。<var-list>。nil,循環終止;否則執行循環體,更新控制變量為第一個返回值,回到第 2 步。等價 while 代碼:
do
local _f, _s, _var = <exp-list> -- 三值初始化
while true do
local var_1, ..., var_n = _f(_s, _var)
_var = var_1 -- 控制變量更新
if _var == nil then break end
<body>
end
end
無狀態迭代器不保存自身狀態,完全依賴 恆定狀態(被遍歷表)和 控制變量(當前索引)計算下一元素。優勢:可在多個循環復用同一迭代器函數,避免創建閉包開銷。
local function iter(a, i) -- a: 恆定狀態(表), i: 控制變量(索引)
i = i + 1
local v = a[i]
if v then return i, v end -- 返回新控制變量 和 元素值
end
function ipairs(a)
return iter, a, 0 -- 迭代器函數、恆定狀態、控制變量初值 0
end
使用示例:
local aTb = {"One", "Two", "Three"}
for i, v in ipairs(aTb) do
print(i, v) -- 1 One, 2 Two, 3 Three
end
function pairs(t)
return next, t, nil -- next 函數、表、初始控制變量 nil
end
next(t, k) 返回表中「下一個」鍵值對(無序)。next(t, nil) 返回第一對。可用 next(t, nil) == nil 判斷表是否為空。
| 特性 | ipairs | pairs |
|---|---|---|
| 適用表類型 | 數組部分(整數鍵 1..n 連續) | 任意表(含哈希部分) |
| 遍歷順序 | 嚴格 1,2,3...n | 無序(實現相關) |
| 遇到 nil 值 | 直接終止遍歷 | 繼續遍歷其他鍵 |
| 返回鍵類型 | 整數索引 | 任意類型鍵 |
| 控制變量初值 | 0 | nil |
記憶口訣:ipairs = integer pairs(只遍歷整數鍵數組部分,有序、遇 nil 止)。
當需要在遍歷過程中修改被遍歷表、或維護複雜遍歷上下文時,可用表保存狀態:
function makeStatefulIter(t)
local state = { data = t, idx = 0, filtered = {} }
return function()
-- 可在此修改 state.filtered、state.idx 等
state.idx = state.idx + 1
return state.data[state.idx]
end, state, 0 -- 將狀態表作為恆定狀態傳遞
end
此模式下,迭代器函數通過 恆定狀態 參數訪問並修改共享狀態表。
function filter(t, predicate)
local i = 0
return function()
i = i + 1
while t[i] ~= nil do
if predicate(t[i]) then return i, t[i] end
i = i + 1
end
return nil
end
end
-- 使用:只遍歷偶數
for i, v in filter({1,2,3,4,5,6}, function(x) return x%2==0 end) do
print(i, v) -- 2 2, 4 4, 6 6
end
for i=1, #t do ... end 最快,避免迭代器函數調用開銷。for k,v in pairs(t) do local ... end,作用域最小化。理解泛型 for 的三值協議,是掌握 Lua 遍歷抽象的鑰匙。無狀態迭代器以「函數不變、狀態外置」的函數式思想,實現了高效與通用的完美平衡,這正是 Lua 設計哲學「機制而非策略」的生動體現。