Lua-迭代器的探究

前言

先代表,我是個正在學習 Lua 的新手,在遇到些學習上的問題,通過思考嘗試,讓後寫下我對相關問題知識的認識。
個人博客:https://crabsaberv.github.io/html

菜鳥教程上學習 Lua 迭代器過程當中,遇到了些障礙。雖然根據菜鳥教程中的解釋:git

下面咱們看看泛型 for 的執行過程:github

  • 首先,初始化,計算 in 後面表達式的值,表達式應該返回泛型 for 須要的三個值:迭代函數、狀態常量、控制變量;與多值賦值同樣,若是表達式返回的結果個數不足三個會自動用 nil 補足,多出部分會被忽略。
  • 第二,將狀態常量和控制變量做爲參數調用迭代函數(注意:對於 for 結構來講,狀態常量沒有用處,僅僅在初始化時獲取他的值並傳遞給迭代函數)。
  • 第三,將迭代函數返回的值賦給變量列表。
  • 第四,若是返回的第一個值爲nil循環結束,不然執行循環體。
  • 第五,回到第二步再次調用迭代函數

可是在本身更具理解,試着編寫一些測試 Lua 無狀態迭代器代碼時,出現的結果卻讓人捉急。
可先閱覽 Lua 官方文檔中對迭代器的描述: Lua 5.3 文檔 -3.3.5
(能看懂官方解釋,基本上本文就沒必要往下看了)shell

經歷

初次編寫

開始根據菜鳥教程編寫了一個無狀態迭代器,固然運行結果也正常。閉包

function square(iteratorMaxCount,currerntNumber)
	if currerntNumber < iteratorMaxCount then
		currerntNumber = currerntNumber+1;
		return currerntNumber,currerntNumber*currerntNumber;
	end
end

for i,n in square,10,0
do
	print(i,n)
end

這段代碼的行爲其實很簡單,就是打印了10之內全部的平方數。
根據菜鳥教程描述的 for 的執行過程可知,suqare 就是迭代器函數,10是狀態常量,0則是控制變量。而當運行時,會先根據 square 以及參數,獲得迭代器的三個元素(迭代函數,狀態常量,控制變量),之後就會利用這三個元素來進行迭代的過程。函數

但這裏菜鳥教程對這裏有些輕描淡寫,並沒說清楚真正各個參數是如何被使用,尤爲是 square 的參數和返回值之間的聯繫或者是否有聯繫,卻沒講清楚。學習

問題的出現

後來,我在想常常性在 for 中用兩個變量來接收,能不能只用一個 臨時變量來接收迭代結果,而且迭代器函數可否只有一個參數,一個返回值。測試

因而有了下面的代碼:ui

function aa(a)
	if a<10 then
		a = a+1
		return a
	end
end

for i in aa,1 do
	print(i)
end

以我最初對迭代器的理解,應該會執行10次運算,然而:lua

2
2
2
.
.
.

無限循環打印 2 。

此時就開始納悶了,這個迭代器的內部原理到底是什麼,aa() 被作了,其中的參數 a 又作了什麼處理,返回的 a 除了賦值給了 i,而後又去了哪裏。

爲了解決這些疑問,我嘗試了不少種不一樣的組合形式,好比將參數設置爲2個,返回值設置成多個,參數調換位置等,但不少時候最後的表現結果卻和預期差的很遠。

沒有辦法,百度不給力,那就只能去看 Lua 的官方文檔了。

果不其然,官方的解釋很是好理解,還給了示例。

官方解釋

參照官方文檔-3.3.5的解釋:

for statement like

for var_1, ···, var_n in explist do block end

is equivalent to the code:

do
   local f, s, var = explist
   while true do
     local var_1, ···, var_n = f(s, var)
     var = var_1
     if var == nil then break end
     block
   end
 end

上面的迭代器遍歷能夠看做是下面的過程:

可見一開始由 do 語句塊將包裹起來,其中會建立三個變量,而這三個變量 f,s,var 正好對應着迭代器三元素:f-迭代器函數,s-狀態常量,var-控制變量。其值由 explist 的返回值得到。這也就證實了爲什麼 for 迭代器 in 後面的最終函數必須以:「迭代器函數,狀態常量,控制變量「 的形式/結構來編寫。

後來在語句塊中編寫一個 while 循環,其內部又用多個局部變量 var_x,來接收 f(s,var) 的結果。而能看出這些 var_x 就是咱們 for 循環中用於接收迭代過程結果的變量。這就構成了一個閉包,f 就是這裏的一個閉包函數。

而下面的語句,將 var_1 接收到的 var 的值又再次賦值給 var。var 是一個外部變量,那麼在下次循環中,f(s,var) 的 var 傳入的就是新值,也便是控制變量的更新。

當 var 爲 nil 時,循環就會跳出。

思考後的結論

注意事項

參考了這些,我認爲對於無狀態迭代器的編寫須要遵照幾個規則

  1. for 迭代中,in 後面必須最終能接收到:迭代器函數,狀態常量,控制變量 ;且順序不能改變。
  2. 咱們自行建立的迭代器方法要保證能正常迭代,必須包含兩個(或至少兩個)參數;同時參數分別表示:"狀態常量,控制變量",順序也不容許改變。
  3. 咱們自建的迭代器方法的返回值的第一個值,會做爲下一次迭代的控制變量參數傳遞進來。而其餘參數只會用作返回賦值給 for 的接收變量。

認識

參照這幾個規則,基本上能保證咱們編寫的無狀態迭代器不會出現什麼超出預期的問題。

而根據分析,也能得出關於迭代器的內部參數特色:

  • for 迭代 in 的後面能夠是個包裝器,方便咱們閱讀,其返回值就是迭代器函數,狀態常量,控制變量 這三個元素。
  • 內部實際上將迭代函數以閉包形式調用。
  • 可見狀態常量只有一開始初始化迭代器三元素的過程當中被使用,以後的時候都是不變的,且直接做爲對於參數被傳遞給迭代器函數。
  • 迭代器函數的返回值有可能多也可能少,總之在填補的時候,沒有的就會填補 nil ,多餘的就被忽視。
  • 迭代器函數的第一個返回值,必須是表示控制變量的值。
  • 要使得迭代函數的迭代過程,只須要使得返回的控制變量的值爲 nil 便可。
  • 最後根據官方描述-3.3.5,for 中可使用 break 中斷結束循環迭代。
相關文章
相關標籤/搜索