天天看點

【lua學習】lua疊代器和泛型for淺析【lua學習】lua疊代器和泛型for淺析

【lua學習】lua疊代器和泛型for淺析

1. 疊代器與Closure:

在Lua中,疊代器通常為函數,每調用一次函數,即傳回集合中的“下一個”元素。每個疊代器都需要在每次成功調用之間保持一些狀态,這樣才能知道它所在的位置和下一次周遊時的位置。從這一點看,Lua中closure機制為此問題提供了語言上的保障,見如下示例:

function values(t)
    local i = 
    return function()
        i = i + 
        return t[i]
    end
end

t = {, , }
it = values(t)
print([[======================]])
while true do
    local element = it()
    if element == nil then
        break
    end
    print(element)
end


--另外一種基于foreach的調用方式(泛型for)
t2 = {, , }
print([[======================]])
for element in values(t2) do
    print(element)
end
           

運作結果:

【lua學習】lua疊代器和泛型for淺析【lua學習】lua疊代器和泛型for淺析

從上面的應用示例來看,相比于while方式,泛型for的方式提供了更清晰的實作邏輯。因為Lua在其内部替我們儲存了疊代器函數,并在每次疊代時調用該隐式的内部疊代器,直到疊代器傳回nil時結束循環。

2. 泛型for的語義:

上面示例中的疊代器有一個明顯的缺點,即每次循環時都需要建立一個新的closure變量,否則第一次疊代成功後,再将該closure用于新的for循環時将會直接退出。

這裡我們還是先詳細的講解一下Lua中泛型(for)的機制,之後再給出一個無狀态疊代器的例子,以便于我們的了解。如果我們的疊代器實作為無狀态疊代器,那麼就不必為每一次的泛型(for)都重新聲明一個新的疊代器變量了。

泛型(for)的文法如下:

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

為了便于了解,由于我們在實際應用中通常隻是包含一個表達式(expr),是以簡單起見,這裡的說明将隻是包含一個表達式,而不是表達式清單。現在我們先給出表達式的原型和執行個體,如:

function ipairs2(a)
     return iter,a,
 end
           

該函數傳回3個值,第一個為實際的疊代器函數變量,第二個是一個恒定對象,這裡我們可以了解為待周遊的容器,第三個變量是在調用iter()函數時為其傳入的初始值。

下面我們再看一下iter()函數的實作,如:

local function iter(a, i)
    i = i + 
    local v = a[i]
    if v then
        return i, v
    else
        return nil, nil
    end
end
           

在疊代器函數iter()中傳回了兩個值,分别對應于table的key和value,其中key(傳回的i)如果為nil,泛型(for)将會認為本次疊代已經結束。下面我們先看一下實際用例,如:

local function iter(a, i)
    i = i + 
    local v = a[i]
    if v then
        return i, v
    else
        return nil, nil
    end
end

function ipairs2(a)
    return iter,a,
end

a = {"one","two","three"}
for k,v in ipairs2(a) do
    print(k, v)
end
           

運作結果如下:

【lua學習】lua疊代器和泛型for淺析【lua學習】lua疊代器和泛型for淺析

這個例子中的泛型(for)寫法可以展開為下面的基于while循環的方式,如:

local function iter(a, i)
    i = i + 
    local v = a[i]
    if v then
        return i, v
    else
        return nil, nil
    end
end

function ipairs2(a)
    return iter,a,
end

a = {"one","two","three"}
do
    local _it,_s,_var = ipairs2(a)
    while true do
        local var_1,var_2 = _it(_s,_var)
        _var = var_1
        if _var == nil then  --注意,這裡隻判斷疊代器函數傳回的第一個是否為nil。
            break
        end
        print(var_1,var_2)
    end
end
           

運作結果同上,對比可知for語句執行的過程:for做的第一件事就是對in後面的表達式求值,這些表達式應該傳回3個值供for儲存:疊代器函數、恒定狀态和控制變量的初值。這裡和多重指派是一樣的,隻有最後一個表達式才會産生多個結果,并且隻會保留前3個值,多餘的值會被丢棄;而不夠的話,就以

nil

補足。

在初始化完成以後,for會以恒定狀态和控制變量來調用疊代器函數。然後for将疊代器函數的傳回值賦予變量清單中的變量。如果第一個傳回值為nil,那麼循環就終止,否則,for執行它的循環體,随後再次調用疊代器函數,并重複這個過程。

3. 無狀态疊代器的例子:

local function getnext(list, node)  --疊代器函數。
    if not node then
        return list
    else
        return node.next
    end
end

function traverse(list)  --泛型(for)的expression
    return getnext,list,nil
end

--Lua 實作連結清單  
node={}  
list=node 

--初始化,建構一個空表  
function init()  
    list.data=""  
    list.next=nil  
end  

--向連結清單的尾部添加資料  
function addRear(d)  
    node.next={}  --建立一個節點,相當于malloc一個節點  
    node=node.next  
    node.next=nil  
    node.data=d  
end  

--清理連結清單,操作完成後,連結清單還在,隻不過為空,相當剛開始的初始化狀态  
function clear()  
    if not list then  
        print('連結清單不存在')  
    end  

    while true do  
        firstNode=list.next  
        if not firstNode then  --表示連結清單已為空表了  
            break  
        end  
        t=firstNode.next  
        list.next=nil  
        list.next=t  
    end  
    list.data=""
    print('-- clear ok --')  
end  

--銷毀連結清單  
function destory()  
    clear() --先清除連結清單  
    list=nil  
end  

init()
--初始化連結清單中的資料。
for line in io.lines() do
   if(line == 'q')
   then
        break
   end
   addRear(line)
end


--以泛型(for)的形式周遊連結清單。
for node in traverse(list) do
    print(node.data)
end

destory()
           

運作結果如下:

【lua學習】lua疊代器和泛型for淺析【lua學習】lua疊代器和泛型for淺析

這裡使用的技巧是将連結清單的頭結點作為恒定狀态(traverse傳回的第二個值),而将目前節點作為控制變量。第一次調用疊代器函數getnext()時,node為nil,是以函數傳回list作為第一個結點。在後續調用中node不再為nil了,是以疊代器傳回node.next,直到傳回連結清單尾部的nil結點,此時泛型(for)将判斷出疊代器的周遊已經結束。

最後需要說明的是,traverse()函數和list變量可以反複的調用而無需再建立新的closure變量了。這主要是因為疊代器函數(getnext)實作為無狀态疊代器。

4. 具有複雜狀态的疊代器:

在上面介紹的疊代器實作中,疊代器需要儲存許多狀态,可是泛型(for)卻隻提供了恒定狀态和控制變量用于狀态的儲存。一個最簡單的辦法是使用closure。當然我們還以将所有的資訊封裝到一個table中,并作為恒定狀态對象傳遞給疊代器。雖說恒定狀态變量本身是恒定的,即在疊代過程中不會換成其它對象,但是該對象所包含的資料是否變化則完全取決于疊代器的實作。就目前而言,由于table類型的恒定對象已經包含了所有疊代器依賴的資訊,那麼疊代器就完全可以忽略泛型(for)提供的第二個參數。下面我們就給出一個這樣的執行個體,見如下代碼:

local iterator
function allwords()
    local state = { line = io.read(), pos =  }
    return iterator, state
end
--iterator函數将是真正的疊代器
function iterator(state)
    while state.line do
        local s,e = string.find(state.line,"%w+",state.pos)
        if s then
            state.pos = e + 
            return string.sub(state.line,s,e)
        else
            state.line = io.read()
            state.pos = 
        end
    end
    return nil
end

--[[
for word in allwords() do
    print(word)
end
]]--