更簡單的 For 迭代器 |
|
目前的語法為
for A, data in iterator_func, X, Y do block end
資料是由函式傳回的實際資料,之後會用在區塊中。下方會對 A、X 和 Y 作進一步的說明。以下是一個基於教學範例的收集迭代器和產生器迭代器的可能實作(盡量寫得詳細,並且從 1 開始):
-- collection iterator -- numbers = {1,3,5,7,9,11,13} function coll_squares(coll) local function next_square(coll, index) if index > #coll then return nil end n = coll[index] return index+1, n*n end return next_square, coll, 1 end for i, square in coll_squares(numbers) do print (square) end --> OK -- generator iterator -- function gen_squares(limit) local function next_square(limit, number) if number > limit then return nil end return number+1, number*number end return next_square, limit, 1 end for n, square in gen_squares(7) do print (square) end --> OK
那麼,什麼是 A、X 和 Y?就收集而言
很難找出一個共同點,以便有意義地說明和命名 A、X 和 Y。在參照手冊中,X 稱為「s」,在教學中稱為「狀態」。在參照手冊中,A 稱為 var1,而 Y 稱為 var。以下是一個讓這些東西有意義的嘗試
上述程式碼可以改寫如下
-- collection iterator -- function coll_squares(coll) local index = 1 local coll = coll -- just to make things clear local function next_square() if index > #coll then return nil end n = coll[index] index = index+1 return n*n end return next_square end for square in coll_squares(numbers) do print (square) end -- OK -- generator iterator -- function gen_squares(limit) local number = 1 local limit = limit -- ditto local function next_square() if number > limit then return nil end n = number number = number+1 return n*n end return next_square end for square in gen_squares(7) do print (square) end -- OK
有一些細微的差異,除了最後一個之外,都是簡化。
我們可以想像更複雜的案例,例如指定產生器時間間隔。其他資料就變成反覆運算器
-- generator iterator -- function gen_squares(start, stop, step) local number = start local function next_square() if number > stop then return nil end n = number number = number+step return n*n end return next_square end for square in gen_squares(3,9,2) do print (square) end --> OK
相同地,如果我們將集合反覆運算器複雜化(這裡相當人為)
-- collection iterator -- require "math" numbers = {1,3,5,7,9,11,13,15,17} function coll_squares(coll, modulo) local index = 1 local function number_filter() -- return next number in coll multiple of modulo, else nil while (index < #coll) do number = coll[index] if math.fmod(number, modulo) == 0 then return number end index = index+1 end return nil end local function next_square() -- yield squares of multiples of modulo in coll n = number_filter() if not n then return nil end index = index+1 return n*n end return next_square end for square in coll_squares(numbers, 3) do print (square) end --> OK
在所有案例中,看來 A、X 和 Y 都不是必要的。利用 lua 基本特性的這種反覆運算器實現方法很好:函數當作值、嵌套函數、封閉值/上值。因此,一個問題是:我們是否能透過移除 A、X 和 Y 來簡化「for」語法、反覆運算器和反覆運算器函數之間的介面?如果是,則可以將新的語法寫成
for data in iterator_func do block end
for A, data in iterator_func, X, Y do block end
結果,反覆運算器的變化不再會以相當複雜的方式受到語法本身的全局捕捉,反而是留給使用者實作。學習和解釋語法,以及針對特定任務寫反覆運算器的正確方法,肯定是更容易的。
參考手冊說明
(第一頁公式作者為 DeniSpir)