本文經過兩個函數完全搞懂Lua中的閉包,相信看完這兩個函數,應該能理解什麼是Lua閉包。廢話很少說,上 code:c++
1 --[[*************************************************************************** 2 3 I'm jacc.kim 4 5 CreateDate: 2017-08-18 11:19:20 6 FileName : closure.lua 7 Version : 1.00 8 Author : jacc.kim 9 Summary : 10 11 ***************************************************************************--]] 12 13 --[[ 14 * summary : 利用閉包實現帶狀態的迭代器 15 * param : collection 集合. 16 * return : !!!重點注意:函數的返回值是重點.返回值是:一!個!函!數!.而該返回的函數是一個閉包. 17 --]] 18 local function elementIterator(collection) 19 -- nIndex 爲 elementIterator() 函數的一個局部變量.標記着 collection 當前處理到的 collection 的下標位置 . 20 -- 這邊爲每一個閉包"實例"初始化它們的"成員變量"的值.(不理解這句的,可先跳過) 21 local nIndex = 0; 22 -- nCollectionCount 爲 elementIterator() 函數的一個局部變量.記錄着命令 collection 的長度信息 23 -- 這邊爲每一個閉包"實例"初始化它們的"成員變量"的值.(不理解這句的,可先跳過) 24 local nCollectionCount = #collection; 25 26 -- 閉包. 27 -- 說明:01.在 Lua 中,函數內部能夠定義函數,被定義的內部函數能夠訪問到外層函數定義的局部變量(如:上面的 nIndex、nCollectionCount、collection等,注意: 28 -- 外層函數的參數其實就是個局部變量). 29 -- 02.而且很是重要的(閉包的特性:)這些外層函數定義的局部變量的狀態會被閉鈕函數保持住.如 nIndex 起初的時候爲 0 值,執行完下面的 30 -- nIndex = nIndex + 1; 後, nIndex 的值爲 1,以後,再次調用該閉包函數時, nIndex = nIndex + 1; 後, nIndex 的值就變爲 2. 31 -- 03.在Lua中,函數是第一類值.所以,函數能夠被賦值給一個變量(局部或全局)、能夠做爲函數參數傳遞、能夠做爲函數的返回值返回.所以,這邊能夠 return 該閉包函數. 32 return function () 33 -- nIndex 爲 外層函數的一個局部變量.但 nIndex 的狀態會被該內部函數(可理解爲閉包函數)記憶保持住.所以,每次調用該閉包函數量.nIndex的值部是不斷累加的. 34 -- (有點相似 C++ 的 static 特性(注意:僅僅只是相似,不能等同).如: 35 -- static auto nIndex = 0; 36 -- nIndex = nIndex + 1; // 或 ++nIndex 37 -- ) 38 nIndex = nIndex + 1; -- feature of Colsure 39 -- nCollectionCount 爲外層函數的一個局部變量.一樣的該變量的狀態也會被保持住.但在該閉包函數中,沒有去改變 nCollectionCount 的值.所以,該值就一 40 -- 值爲集合 collection 的長度. 41 42 --[[ 43 -- 如今重點分析一下這個閉包函數.在此以前,先再介紹另一個Lua的特性.在Lua中,一個函數能夠沒有返回值、能夠只返回一個值、也能夠返回多個值.重點就是要看上下文本 44 -- 到底須要一個函數返回多少個值出去.Ok,這點說明暫時先到此爲止.下面先分析一下該閉包函數.當調用該閉包函數時, 45 (!!!這點很重要啊)若是下面的 if 語句條件成立,則該函數會有一個返回值,返回外層函數的參數(collection)的對應索引位置處的元素. 46 (!!!這點更加劇要)若是下面的 if 語句條件不成立,則該閉包函數是沒有返回值,或者根據上下文,能夠返回多個的 nil 值. 47 --]] 48 if (nIndex <= nCollectionCount) then 49 return collection[nIndex]; 50 end 51 end 52 end 53 54 --[[ 55 * summary : 測試上面設計的利用閉包實現的迭代器 56 * param : collection 待測試的集合. 57 * !!!note : 01.此處的目的:經過該函數,咱們完全理解閉包在此處起到的做用. 58 --]] 59 local function traversalCollection() 60 -- collection 爲測試數據. 61 local collection = { "apple", "pear", "orange" }; 62 -- 這是 Lua 的一個泛型 for 循環.完了完了,沒辦法理解該for循環?不怕,下面咱們來完全搞懂它(注意:搞懂該for循環,咱們也就完全理解什麼是閉包).請看下面的註釋. 63 --[[ 64 for 循環的定義,其實能夠理解爲: 65 for (條件) 66 do 67 // some code here........ 68 end 69 若是for的條件成立,則會執行for後面的語句塊 do ... end 中的語句.不然for循環結束. 70 此處將下面for循環改寫成另一種"等價"的寫法 71 local iter = elementIterator(collection); 72 for element = iter() do 73 print(element); 74 end 75 或者乾脆改寫成 while 語句更容易理解 76 local iter = elementIterator(collection); -- elementIterator()函數其實就比如一個工廠同樣,調用一次,產生一個閉包"實例" 77 local element = iter(); -- 注意:此處 iter() 就是在調用閉兇函數 78 while element do 79 print(element); 80 element = iter(); -- 注意:此處 iter() 就是在調用閉兇函數 81 end 82 前文已在 elementIterator() 中的閉包函數中詳細說明了閉包函數的特性,閉包函數會保持外層函數的狀態信息.所以,調用 iter() 函數將返回一個 collection 集合的 83 元素,再調用一次將返回下一個元素,一直調用,最後因元素遍歷結束,因此最終會返回一個nil被 element 接收.此時 while 循環結束(注意:Lua中 nil 與 false 都認爲是假). 84 至此,至關閉包你們也都理解了. 85 --]] 86 for element in elementIterator(collection) do 87 print(element); 88 end 89 end 90 91 traversalCollection(); 92 93 --[[ 94 上文比較神奇的地方有兩處: 95 一爲 local iter = elementIterator(collection); 中的 elementIterator(collection) 96 解釋:elementIterator(collection) 有點相似一個工廠.調用一次則會 產生一個閉包"實例"(該實例"地址"被保存到 iter 變量).而該閉包"實例"擁有 97 nIndex、nCollectionCount、collection "成員"變量.這些變量是它的本身的狀態屬性. 98 若是此時再調用一次 elementIterator(collection) 則又會產生一個新的閉包"實例".每一個閉包實例都將有本身的一份狀態屬性.這些狀態屬性的初始化,都是 99 在閉包函數所在的外層函數處進行初始化(具體可見前文註釋). 100 101 二爲 element = iter(); 中的 iter() 102 解釋:iter() 簡單一點理解,其實就是在調用一個函數而已,僅此而已.而後這個函數返回一個值.只是這個函數比較特別,它有點像c++中的類同樣,有本身的狀態屬性,調用 103 時,它會根據本身所記錄的狀態屬性數據,進行相應邏輯處理,並返回對應的值. 104 105 總結: 106 Lua中的閉包,說它是一個(相似c++中的)類,它不是; 107 Lua中的閉包,說它是一個函數,它也不徹底算是.只是它有點像c++中的函數,而且函數中有一些靜態局部變量同樣.但這樣理解也不徹底對.由於c++中的靜態局部變量 108 在整個程序生命週期內,僅一份實例(處於全局靜態存儲區中); 109 Lua中的閉包,實際上是像c++中的lambda的,但它們也不能徹底等價; 110 111 那麼Lua中的閉包到底像什麼?我的認爲它與c++中的仿函數是同樣的.由於它們使用上都像函數,但又均可以持有本身的狀態屬性數據.並且又均可以產生任意多個的"實例"。 112 --]]