2014-03-28 59 views

回答

6

首先讓我們商定一個定義:(在Lua中)一個迭代器是一個函數 -like對象,每次調用時它都會返回一個序列中的下一個值。我認爲它有助於改寫for迭代因爲做的是Lua的參考手冊:

for itemlist in expression do block end 

是邏輯上等同於(僞):

do 
    local func, seq, controlVar = expression 
    while true do 
     local itemlist = func(seq, controlVar) 
     if first of itemlist == nil then break end 
     controlVar = first of itemlist 

     block (which uses items in itemlist) 
    end 
end 

其中expression是數據的三重(或函數調用返回這樣的三重峯):

  • func是實際的迭代函數
  • seq被遍歷
  • controlVar序列是循環控制變量

迭代狀態可能需要找到序列中的下一個項目上迭代的任何狀態。因此無狀態迭代器是func不包含任何這樣的狀態:您可以隨時調用func(seq, controlVar),返回值將始終相同(如果seq未更改);它不取決於通話之前發生的事情。

如上所示,Lua支持一個循環控制變量。所以爲了讓一個序列可以通過無狀態迭代器迭代,必須有可能根據一個循環控制變量來確定序列中的下一個項目。也就是說,只能從「(s,controlVar)」中找出「下一個項目」。 ipairs()生成一個迭代器:ipairs(s)返回三元組(iterFunction, s, 1);可以給出iterFunctions和索引1,並且返回2, s[1],然後3, s[2]等(最終nil, s[N]用於N個項目的表格)。

如果發現序列中的下一個項目需要多個循環控制變量,該怎麼辦?還是取決於其他變量的狀態,在迭代過程中應該保存這些變量?例如:

  • 一個無盡的迭代器可能需要跟蹤「第一個」項目,以便一旦達到序列的末尾,它就可以在第一個項目處恢復;
  • 圖迭代器可能需要在深度優先搜索中跟蹤「最近的同胞」,以便一旦到達分支的末尾,它就可以繼續下一個最近的同胞。

有狀態迭代器持有有關迭代的狀態,以便可以找到下一個項目。在Lua中,如果迭代器函數是閉包(具有upvalues的函數)或仿函數(表的行爲如函數,即具有__call元方法),則這是可能的。 up值(閉包)或數據成員(函子)可以存儲所需的狀態。

無狀態迭代器總是可以包裝到有狀態迭代器中。對於ipairs

function statefulIpairs(s) 
    local f, s, var = ipairs(s) 
    return function() 
     local i, v = f(s,var) 
     var = i 
     return i, v 
    end 
end 

這可以被稱爲

tbl = {'a', 'b', 'c', 'd'} 
sip = statefulIpairs(tbl) -- sip is stateful iter specific to tbl 
for i,v in sip() do print(i,v) end 

有狀態迭代的開發人員決定哪些功能迭代器有:迭代器的API可允許退,反轉方向,或其他操作。這甚至可以在關閉的情況下使用:可以使用附加參數來訪問附加功能。例如,接受第三個參數,當非爲零,重置序列的開端:

function resetableStatefulIpairs(s) 
    local f, s, var = ipairs(s) 
    local start = var 
    return function(a,b,reset) 
     if reset ~= nil then var = start; return end   
     local i, v = f(s,var) 
     var = i 
     return i, v 
    end 
end 

sip = resetableStatefulIpairs(tbl) -- sip is stateful iter specific to tbl 
for i,v in sip() do print(i,v) end 
sip(nil, nil, true) -- reset it 
for i,v in sip() do print(i,v) end 

更新了一個更簡潔的例子是如何產生的功能迭代器,接受命令,這樣你可以」 ...停止在序列的其餘部分的序列中的任意位置和迭代的3倍」(由@deduplicator的要求):

function iterGen(seq, start) 
    local cvar = start or 1 
    return function(cmd) 
     if cmd == nil then 
      if cvar > #seq then return nil, nil end 
      val = seq[cvar] 
      cvar = cvar + 1 
      return cvar-1, val 

     else 
      cmd = cmd[1] 
      if cmd == 'rewind' then 
       cvar = start or 1 

      elseif cmd == 'newstart' then 
       start = cvar 
      end 
     end 
    end 
end 

通過以上:

> s = {1,2,3,4,5,6,7} 
> iter = iterGen(s) 
> for i,v in iter do print(i,v); if i==3 then break end end 
1  1 
2  2 
3  3 
> iter {'newstart'} -- save current as the new start pos 
> for i,v in iter do print(i,v) end -- continue till end 
4  4 
5  5 
6  6 
7  7 
> iter {'rewind'} 
> for i,v in iter do print(i,v) end 
4  4 
5  5 
6  6 
7  7 
> iter {'rewind'} 
> for i,v in iter do print(i,v) end 
4  4 
5  5 
6  6 
7  7 

正如所證明的,除了迭代狀態在迭代器內部這一事實之外,狀態迭代器沒有什麼特別之處,因此,如上所述,開發人員需要公開所需的功能,如上面的rewind和newstart。使用無狀態迭代器,沒有限制。

這將是一個更自然的API設計迭代器作爲函子,因爲然後迭代器「函數」有「方法」可以調用,但創建一個可指令函數是一個有趣的挑戰。

+0

非常感謝你,我感謝你的回答,否則解釋它像我這樣的noob真的很難:)謝謝:) –

+1

@Simrankaur很高興這對你有意義。既然你有足夠的積極點,你應該嘗試提出答案或評論什麼是不明確的,如果作者可以修復 - 這提高了SO質量。乾杯! – Schollii

2

狀態和無狀態的迭代器之間的區別很簡單:

有狀態的迭代器有內部狀態,所以你不能創建它們,運行它們了一下,然後反覆要求使用相同的迭代器序列的末端。 string.gmatch(...)返回一個很好的例子。

相反,無狀態迭代器是其輸入的純函數,所有狀態都是外部的。最有名的是返回pairs(a)(返回a, next,如果沒有__pairs metamethod定義)和ipairs(如果沒有`__ipairs metamethod)。如果你想重複遍歷序列的末尾,只保存參數。

+0

有趣,但我不明白這:)有狀態的迭代器_cannot_重複請求的序列結束?你能舉一個例子,說明你可以用'gmatch'做什麼,如果它是無狀態的而不是有狀態的?另外,你怎麼知道'gmatch'是有狀態的?如果你可以擴大你的答案(而不是評論),那將非常感激。 – Schollii

+0

看看lua文檔中有關string.gmatch的第一句話(版本5.2)。 – Deduplicator

+0

AFAICT(來自測試),即使沒有引用留給字符串(因此函數中存在引用),也可以從'gmatch'中存儲返回的函數並在將來隨時調用它。 – Schollii

相關問題