從例子中學習閉包和範式for

又翻起了programming in lua這本書了,而且又看到 感謝貢獻之人這段代碼,今天就對這段代碼做下分析。代碼如下:

-- file: 'thanks.lua'
-- desc: to print the list of the contributing guys
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
helpful_guys = {
    "----參與翻譯----",
    "buxiu", "鳳舞影天", "zhang3",
    "morler", "lambda", "sunlight",
    "\n",
    "----參與校對----",
    "鳳舞影天", "doyle", "flicker",
    "花生魔人", "zhang3", "Kasi",
    "\n"
}
for e in list_iter( helpful_guys ) do
     print(e)
end

對於這段代碼,我們分兩個部分來講,第一部分是閉包,第二部分是範式for的運行過程.

首先先講閉包,簡單一點說,閉包就是 一個內部函數,它可以訪問一個或多個外部函數的外部局部變量
結構:包含兩個函數,一個是閉包自己,一個是工廠(創建閉包的函數)

-- 這裏list_iter函數是創建閉包的工廠
function list_iter( t )
    local i = 0
    local n = table.getn( t )
    -- 匿名函數,list_iter函數的返回值
    return function ()
        -- 變量i、n和t相對於這個匿名函數,是個外部函數(list_iter)的局部變量
        i = i + 1
        if i <= n then return t[i] end
    end
end

先看下上面的list_iter函數,它的返回值是一個函數,這個函數保存着三個外部的局部變量i,n和t
大家想一下:

fFunc = list_iter( {"a", "b"} )

運行list_iter的返回值就是一個函數, 它保存了三個值,i=0, n=2, t={“a”, “b”}
第一次運行fFunc函數, 即

 print( fFunc() )

輸出結果爲 a
因爲 fFunc 它是一個函數,運行的時候
i=i+1 這時i=0+1=1
t[1] 就是 a

運行完之後,這個fFunc的保存的 外部的局部變量 i變成了1

同理再運行一下 fFunc時,
輸出的結果爲 b
同時,這個fFunc的保存的 外部的局部變量 i變成了2

這時候再運行一下的時候,結果就是nil, 因爲
i=2+1 等於 3
i<=n不成立了(3<=2不成立)

所以接下來運行的時候,結果都是爲nil,
但是每運行一次的時候,fFunc這個閉包的局部變量 i就會加1

function iter_list( t )
    local i = 0
    local n = table.getn(t)
    return function()
        i = i + 1
        print( "i=", i )
        if i<= n then
            return t[i]
        end
    end
end
fFunc = iter_list( {"a", "b"} )
print( fFunc() )
print( fFunc() )
print( fFunc() )
print( fFunc() )

運行結果:
這裏寫圖片描述

接下來講一下範式for的運行過程
其語法如下

for <var-list> in <exp-list> do
    <body>
end

其中
var-list 是一個或多個以逗號分割的變量名列表
exp-list 是一個或多個以逗號分割的表達式列表,迭代函數(閉包),狀態常量和控制變量
通常情況下 exp-list 爲:迭代工廠的調用。 即: iter_list() 其返回值是一個閉包

其運行過程爲:

do
    -- _f爲迭代函數,_s爲狀態常量,_var爲控制變量
    local _f, _s, _var = explist
    while true do
        -- 迭代函數的參數有兩個
        -- 第一個是狀態常量_s, 這個值是explist返回的第二個參數
        -- 第二個是控制常量,第一次的值是explist返回的第三個參數,後面的值是上次運行_f返回的第一個返回值
        local var_1, ... , var_n = _f( _s, _var )
        -- 接下來_f的第二個參數,變成上一次運行_f返回的第一個值
        _var = var_1
        -- 如果控制變量爲nil就結束
        if _var == nil then 
            break 
        end
        block
    end
end

上面的解釋可能一開始看不懂,沒關係,接下來根據實例來說明一下,這裏用最開始那個感謝的函數來說明:

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
helpful_guys = {
    "----參與翻譯----",
    "buxiu", "鳳舞影天", "zhang3",
    "morler", "lambda", "sunlight",
    "\n",
    "----參與校對----",
    "鳳舞影天", "doyle", "flicker",
    "花生魔人", "zhang3", "Kasi",
    "\n"
}

-- 這裏 in 後面的結果,我們希望是一個迭代函數,一個狀態常量,一個控制常量
-- 但是這裏是 list_iter( helpful_guys )
-- 也就是說 local _f, _s, _var = list_iter( helpful_guys )的返回值
-- 因爲list_iter只有一個返回值,所以這裏的三個變量分別爲
-- _f 爲閉包, _s = nil, _var = nil
for e in list_iter( helpful_guys ) do
    -- 第一次運行_f的時候,返回值是helpful_guys的第一個元素;"----參與翻譯----"
    -- 根據上面分析for的運行過程,可知,這裏就是一直運行_f函數,直到其返回值爲nil就就結束
    print(e)
end

上面的

for e in list_iter( helpful_guys ) do
    print( e )
end

等價於

_f, _s, _var = list_iter( helpful_guys )
for e in _f, _s, _var do
    print( e )
end

運行的_f的過程中,其實是有傳入狀態常量_s,和後續變化的控制變量_var,只是這裏閉包函數沒有參數而已

我們可以修改一下迭代工廠,就可以看到狀態常量_s和控制變量_var

function list_iter( t )
    local i = 0
    local n = table.getn( t )
    -- _s爲狀態常量,其值是迭代工廠返回的第二個值,這裏就是 "我是狀態常量"
    -- _var爲控制變量,第一次是迭代工廠返回的第三個值,後續是閉包運行返回的第一個值
    return function ( _s, _var )
        print( "狀態常量:", _s )
        print( "控制量:", _var )
        i = i + 1
        if i <= n then return t[i] end
    end, "我是狀態常量", "我是初始的控制變量"
end

for e in list_iter( {"a"} ) do
    print(e)
end

運行結果:
這裏寫圖片描述

上面就是從例子中學到的閉包和範式for的運行過程,有什麼問題,歡迎留言討論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章