第一版是為 Lua 5.0 編寫的。儘管在很大程度上仍然適用於後續版本,但仍有一些不同之處。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您還可以幫助 支援 Lua 專案


7.1 – 反覆運算器和封閉

反覆運算器是任何允許您反覆運算集合中元素的建構。在 Lua 中,我們通常用函式表示反覆運算器:每次呼叫該函式時,它會傳回集合中的「下一個」元素。

任何反覆運算器都需要在連續呼叫之間保留一些狀態,以便它知道它在哪裡以及如何從那裡繼續。封閉提供了一個絕佳的機制來執行此任務。請記住,封閉是一個函式,它會存取其封閉函式中的其中一個或多個局部變數。這些變數會在連續呼叫封閉時保留其值,讓封閉記住它在遍歷中的位置。當然,要建立一個新的封閉,我們也必須建立它的外部局部變數。因此,封閉建構通常涉及兩個函式:封閉本身;以及一個工廠,也就是建立封閉的函式。

舉一個簡單的例子,讓我們為一個清單撰寫一個簡單的反覆運算器。與 ipairs 不同,此反覆運算器不會傳回每個元素的索引,只會傳回值

    function list_iter (t)
      local i = 0
      local n = table.getn(t)
      return function ()
               i = i + 1
               if i <= n then return t[i] end
             end
    end
在此範例中,list_iter 是工廠。每次我們呼叫它時,它會建立一個新的封閉(反覆運算器本身)。該封閉會在其外部變數(tin)中保留其狀態,以便每次我們呼叫它時,它會傳回清單 t 中的下一筆值。當清單中沒有更多值時,反覆運算器會傳回 nil

我們可以使用此類反覆運算器搭配 while

    t = {10, 20, 30}
    iter = list_iter(t)    -- creates the iterator
    while true do
      local element = iter()   -- calls the iterator
      if element == nil then break end
      print(element)
    end
不過,使用一般 for 會比較容易。畢竟,它就是為這種反覆運算而設計的
    t = {10, 20, 30}
    for element in list_iter(t) do
      print(element)
    end
一般 for 會處理反覆運算迴圈中的所有簿記:它會呼叫反覆運算器工廠;在內部保留反覆運算器函式,因此我們不需要 iter 變數;在每次新的反覆運算時呼叫反覆運算器;當反覆運算器傳回 nil 時停止迴圈。(稍後我們會看到一般 for 實際上會執行更多操作。)

作為一個更進階的範例,我們將撰寫一個反覆運算器來遍歷目前輸入檔案中的所有字詞。若要執行此遍歷,我們需要保留兩個值:目前行數和我們在該行中的位置。有了這些資料,我們永遠可以產生下一個字詞。為了保留這些資料,我們使用兩個外部局部變數,linepos

    function allwords ()
      local line = io.read()  -- current line
      local pos = 1           -- current position in the line
      return function ()      -- iterator function
        while line do         -- repeat while there are lines
          local s, e = string.find(line, "%w+", pos)
          if s then           -- found a word?
            pos = e + 1       -- next position is after this word
            return string.sub(line, s, e)     -- return the word
          else
            line = io.read()  -- word not found; try next line
            pos = 1           -- restart from first position
          end
        end
        return nil            -- no more lines: end of traversal
      end
    end
迭代器函數的主要部分是呼叫 string.find。此呼叫會從目前位置開始,在目前行中搜尋一個字詞。它使用模式 '%w+' 來描述「字詞」,這個模式會比對一個或多個字母數字字元。如果找到字詞,函數會將目前位置更新為字詞後的第一個字元,並傳回該字詞。(string.sub 呼叫會從 line 中萃取一個子字串,位置介於給定的位置之間)。否則,迭代器會讀取新的一行,並重複搜尋。如果沒有更多行,它會傳回 nil 來表示迭代結束。

儘管 allwords 很複雜,但它的用法很簡單

    for word in allwords() do
      print(word)
    end
這是迭代器常見的情況:它們可能很難撰寫,但很容易使用。這不是什麼大問題;通常,使用 Lua 進行編程的最終使用者不會定義迭代器,而只會使用應用程式提供的迭代器。