新的 For

lua-users home
wiki

摘要:這是關於在 Lua 5.0 中推出的擴充「for」語法,其中採用 iterator 形式。這個頁面可說是具有歷史意義(不再是新功能了)。在參考手冊[1]中,有更完整的說明。

簡介

我(PhilippeLhoste)稍微玩了一下 Lua 5.0 中的「for」新語法(若語法或行為有變更,將在工作 0 中說明)。

我從 Roberto 在 2002/06/06 發出的郵件開始,其中提供了偽碼。由於我比較喜歡使用較長的變數名稱,因此稍加改寫,以利(我)理解。以下引用此郵件,作為提醒。

偽碼

for var1, ..., varn in expList do
  block
end

等同於以下內容[1][2](Lua 5.1)

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,我們會傳回第二個部分

如果 assert 沒問題,則會傳回其表達式。

在任何情況下,「and nil」都會將此部分變更為 nil,因此迴圈就會結束。

試驗「for」語法

我開始像這樣改寫

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` 就是一個範例。在「資料量較繁重」的迴圈中,一個額外物件的成本可以忽略不計。例如,在檔案範例中,你必須先開啟檔案、產生檔案處理程式、產生多個字串(各行資料)等等。一個額外封閉函數(或表格)不會對總消耗造成多大的影響。

相關連結


RecentChanges · preferences
edit · history
上次編輯時間:格林威治標準時間 2008 年 3 月 29 日 下午 9:58 (diff)