新的 For |
|
for
」語法,其中採用 iterator 形式。這個頁面可說是具有歷史意義(不再是新功能了)。在參考手冊[1]中,有更完整的說明。
我(PhilippeLhoste)稍微玩了一下 Lua 5.0 中的「for」新語法(若語法或行為有變更,將在工作 0 中說明)。
我從 Roberto 在 2002/06/06 發出的郵件開始,其中提供了偽碼。由於我比較喜歡使用較長的變數名稱,因此稍加改寫,以利(我)理解。以下引用此郵件,作為提醒。
偽碼
for var1, ..., varn in expList do block end
do local _func, _state, var = <explist> while 1 do local var1, ..., varn = _func(_state, var1) var = var1 if var == nil then break end <block> end end
explist
是兩個或三個表達式:函式(_func
)、狀態(資料持久化)和初始值。它可以是傳回這些值的函式。
_func
傳回 explist
必須傳回 n
個值,當程序執行完畢,第一個值為 nil
。
我嘗試了以下程式碼(也是由 Roberto 提供)
t = { "a", "b", "c" } for i, k in nexti(t) do print(i, k) end for k, v in next, t do print(k, v) end for k, v in t do print(k, v) end
結果是相同的。
在第一行中,'next
' 傳回函式 iterator 和當作參數的表格。
在第二行中,'next
' 是函式(_func
),而 't
' 是狀態(_state
)。
第三行為上述語句的語法糖,以確保向後相容性。
Roberto 提供的讀取檔案列的程式碼片段,幫助我理解上述偽碼(我有點遲鈍...)。
程式碼如下:
function lines(filename) local f = assert(io.open(filename, "r")) return function () return f:read() or (assert(f:close()) and nil) end end for line in lines(file) do print(line) -- Or process line, etc. end
對於想知道匿名函式傳回值得人來說
f:read()
讀取一列,則不會評估「or」之後的部分。
f:read()
會是 nil
,我們會傳回第二個部分
and nil
」都會將此部分變更為 nil,因此迴圈就會結束。
我開始像這樣改寫
function lines(filename) local f = assert(io.open(filename, "r")) local i = 0 return function () i = i + 1 return f:read() or (assert(f:close()) and nil), i end end local line, number for line, number in lines(file) do print("(" .. number .. ") " .. line) end
我們已經有狀態持久化... 我認為這應是 5.0 封閉部分的魔力,lines() 中匿名函式的「i
」指向區域變數。
我做出最終版本,使用狀態和初始值,如同偽碼所示
function lines(filename) local f = assert(io.open(filename, "r")) local state = {} state.persistentValue = " " state.counter = 0 return function (_state, _previousValue) _state.persistentValue = "." .. _state.persistentValue _state.counter = _state.counter + 1 print(_state.persistentValue .. _previousValue) return f:read() or (assert(f:close()) and nil), _state.counter end, state, "First value" end
但我不確定這個狀態與前一個版本相比,有什麼優點... 它看起來更像物件導向,而我可能會錯過一些副作用。
羅伯托·傑魯薩林斯基則答道:這純屬個人喜好。使用狀態的主要優點在於,你執行 for 迴圈時,不需要產生任何新的「物件」(表格、封閉函數等)。`nexti
` 就是一個範例。在「資料量較繁重」的迴圈中,一個額外物件的成本可以忽略不計。例如,在檔案範例中,你必須先開啟檔案、產生檔案處理程式、產生多個字串(各行資料)等等。一個額外封閉函數(或表格)不會對總消耗造成多大的影響。