- [!] 版本公告:本文中描述的問題已得到解決。從 5.0 版開始,n 值不再作為表格列表部分的大小計算中所引用的內容。
問題
表格包含成員「n」,作為表格插入的優化。也就是說,取自手冊:
- getn (table)
- 視為列表時,傳回表格的大小。如果表格有數值值的
n
欄位,該值即為表格的大小。否則,大小即為表格中具有非 nil 值的最大數值索引。可以使用 Lua 定義此函數
function getn (t)
if type(t.n) == "number" then return t.n end
local max = 0
for i, _ in t do
if type(i) == "number" and i>max then max=i end
end
return max
end
由於表格的雙重特性,也就是可以同時作為列表和字典,因此「n」可能會與表格中的使用者資料相衝突。以下部分會列出一些問題解決方案。歡迎在解決方案旁留言或提出您自己的見解。請留下您的姓名或縮寫,以便計算「投票」。
這可以用更好的方式定義。n 欄位並非「表格大小」,也非用於「表格插入」。(事實上,Lua API 函數名稱及其說明文件讓這件事變得更令人困惑。)在 Lua 中,表格是純粹的資料類型,而列表並非如此。有許多種方法可以在表格資料類型上實作列表。由於列表是重要的資料類型,而且甚至在 VM 內部也是必需的 (用於變長引數),因此標準程式庫提供實作,其中包括使用 n 欄位來表示列表大小,以及演算法以設定任意表格的「列表部分」(當沒有 n 欄位時,getn
便會執行此動作)。
其他解決方案
這不是問題,別管它。
您的程式設計風格可能不需要在同一個表格中混合列表和字典,所以這可能不是問題。
- 問題並非在於我的程式設計風格不允許在同一個表格中混合向量和字典;而是,我不認為在同一個表格中混合不受控建與受控建是個好主意。遲早,無論受控建稱為是 n、__n__ 或是 ____NUMBER_OF_NUMERIC_KEYS____,它都會為你招致禍害。我不喜歡像 __n__ 這樣的名稱,從美學角度和「藉由不可能性來避免」的觀點來看,這只會讓錯誤更難找。混合受控建 (例如巢位名稱) 又是另一個問題,但是我對於不將我的巢位稱為「n」完全沒有意見。此外,從事會造成向後相容性問題的變更並非輕而易舉的事情。-- RiciLake
- 因此,基本上您不想要破壞向後相容性,您會發現由於表大小名稱為「n」,任何衝突錯誤都容易找到,而且您不介意表格大小始終稱為「n」。為什麼不移除問題,正如您所說,「遲早會造成您困擾」,您可以在命名方面保持彈性?我不記得看過任何使用 n 設定表大小的程式碼(因此沒有向後的問題?)。--NDT
- 我認為這麼摘要就夠了 :-)。如果將表用作向量,我不會將其用作字典,儘管我可能在其中放入自己的鍵。在這種情況下,我根本不會使用鍵
n
或任何數字鍵。這與將鍵放入實作為表的任何物件中沒有什麼不同;您必須避免使用物件定義的鍵,希望這些鍵有文件記錄。事實上,我確實有使用 n
設定表大小的程式碼——除非您看過存在的每一行 Lua 程式碼,否則我不認為您可以輕率地宣稱沒有向後問題。在任何情況下,我認為使用 vec.n
而不是 getn(vec)
來檢索表大小很常見,因為如果您能確保該鍵存在(甚至 vec.n or getn(vec)
),這樣會顯著加快速度。—— RiciLake
- 「希望有文件記錄」 :-) 可以避免這種混淆。表是多用途的這個事實,可以用作清單或字典,這表示不應套用此類型的限制。就我個人而言,我寧願修復可能因這個限制而損壞的程式碼。我認為 Lua 作為一種語言還處於起步階段,它的根源在於便利的嵌入和組態。它仍然有一些小怪癖,例如這個問題,在被視為嚴肅的指令碼語言之前,必須將其徹底解決。我不確定成為「Python 打敗者」是否是一個設計目標,但我確信作者渴望看到這門語言及其使用者群體的發展。隨著 Lua 的改進,這將持續發生。 :-) --NDT
- 如果您不喜歡 getn,請定義您自己的 getn、tinsert 和 tremove 版本。就這樣。只有在可變參數函式的引數表中,還有一個其他地方使用「n」;只要說 arg 不是清單而是表即可 ;-) 但請參閱以下以了解另一個概念(len[t])。—— ET
- 公平地說,還有幾個其他地方,其中之一是
call
。然而,撰寫取代函式絕對夠容易,而且沒有人強迫您使用 tinsert 和 tremove—— RiciLake
- 我的觀點更像是「這是一個問題,請保留它」。我還沒看到一個好的解決方案。
setn
的概念是堆疊在一個堆疊上。—— JohnBelmonte
它應該重新命名
「n」變數應重新命名為較不容易衝突的變數,例如「__n__」
- 任何直接的命名空間入侵都很醜陋且不具有普遍性,以至於它可能會損害實際世界的功能。例如,如果有人想使用表格的前映射網域作為作業系統殼層的環境變數,那就必須以某種方式限制名稱以排除選來用於表格大小的名稱。在內部,這個名稱不必是合法的 Lua 變數名稱。因此,其實空的指標或其他哨兵會比較好,這樣就不會入侵 Lua 變數命名空間。這可能會導致代碼中出現一些例外,不過,由於它們與非法的可能值重疊,因此今天應該要檢查這些例外。--Paul Hsieh
- 使用表格[0]。任意選出的非數字名稱都有可能入侵表格的命名空間,以供非清單用途。不過,由於表格已經包含清單,因此它已經拒絕所有正整數作為索引(表格[1]、表格[2] 等),所以再加入 0 並不會造成額外入侵,遠比「n」少 -- PeterHill
setn()
setn()
將會是搭配 getn()
的一個較好的解決方案。
- 我想這是最好的選擇,它排除了任何衝突的可能性,並允許實際底層實作有一些變化 --Tom Wrensch
- 這是我的最愛 - 設定一個無法透過一般索引存取的值... 可以在內部透過一個弱表格來實現 - 甚至公開那個表格,雖然我認為一個 setn() 函數在可讀性方面會更好 --PeterPrade
- 這看起來像個更好的方法。我不喜歡任意的事情強加於我,無論要編寫程式碼來解決它有多容易。--Terence Martin
- 可能是最好的方法。--LavergneThomas?
- 我也喜歡這種方法。我厭倦了在我的 foreach() 迴圈中解決「n」的問題。搭配 getn() 有道理。下面的方法非常聰明,我當然可以接受,但我認為對於新使用者來說,可能會有點反直覺。
- 這是 IMHO 最好的解決方案,因為我確實認為加入一個「內部」欄位非常危險。想像一下有一個包含在檔案中找到的所有空格分隔字串的表格。這種「隱藏」欄位很危險,即使是以「不可能」的方式命名。-- Vincent Penquerc'h
len[t]
- 好,這對於擁有弱表 (基本概念來自 Luiz Carlos Silveira) 的 Lua 4.1 有用:將清單長度 (或任何其他物件) 儲存在全域表中,亦即稱為「len」。要存取物件「x」的長度,只要寫下「len[x]」,而非「x.n」或「getn(x)」、「setn(x,n)」。你甚至可以設定「len」的索引標籤方法,以在不存在時計算長度。此外,如果你擔心可能會輸入 () 而非 [],請也設定呼叫方法 ;-) -- ET
- 我也喜歡這個想法的巧妙之處。而且它有許多優點:它在清單的內部或可見實作中不會遺留任何雜物,因此你可以反覆運算純清單,而不用擔心忽略「n」、「__n__」或「whatever」鍵。表查詢的執行速度比程序呼叫快,因此它可能會比目前的實作快。此外,它與沒有「index」標籤方法的表相容
settagmethod(tag({}), "index",
function(v, k) if k == "n" then return len[v] end end)
- 因此,我願意改變我的投票 --RiciLake
- 這也與「getn」及「setn」相容;如果那是你想要的,只要包含
function getn(v) return len[v] end
function setn(v, n) len[v] = n end
- 我想將我的投票變更為此。弱鍵長度表會將「n」從表分離,並保留「純」表。如 John 所指出的,清單是表的用途,而這個實作會將清單用途與表分離。「getn()」/「setn()」仍可使用。或許「len[x]」應該改為「tlen[x]」,以保持與「tinsert()」/「tremove()」的一致性。-- Nick Trout
- len[] 會將全域名稱對應到整數嗎?還是會將物件的網域對應到整數?我是 Lua 新手,因此如果我誤解了,請隨時編輯此處或我的其他評論,豪不吝嗇。--Paul Hsieh Paul:len[] 是一個映射,用於將映射物件 (作為弱鍵) 映射到其「清單長度」(如果它們曾被視為清單之前,亦即已呼叫 getn() 或 tinsert() 等,並以此表物件作為參數) -- PeterPrade
- 為了此事而新增全域變數,此一事實讓我覺得與在表新增隱藏欄位類似,儘管它比較不具侵略性。當然,新增 setn 也會引進全域變數,但我認為它比較不太像是駭客行為,因為它是一個 API 條目,而不是你可以將令人尷尬的事物丟棄到某處的地方。-- Vincent Penquerc'h
請新增任何其他解決方案...
實作無法被覆寫的 getnEx() 函數
- 我不瞭解能設定表格大小為與實際大小不同的功能之需求。我建議改用未公開的真正最大整數網域成員,由程式碼中稱為 getnEx() 的功能追蹤每個表格。如此一來,為了維持向下相容性,getn() 能實作為
function getn (t)
if type(t.n) == "number" then return t.n end
return getnEx(t) -- internal function
end
如此一來,人們就能忽略令他們困擾的 getn() 功能,並改用不需要解決方法的 getnEx() 功能。getnEx() 實質上等於
function getnEx (t)
local max = 0
for i, _ in t do
if type(i) == "number" and i>max then max=i end
end
return max
end
--Paul Hsieh
已投選票
請更新下方清單。如果你偏好匿名投票,只需在下方新增一票即可(但我們很樂於聽聽你的意見 :-)。
- 維持現狀:1 票。
- 變更「n」:1 票
- 實作不可覆寫的 getnEx() 功能:1 票
- 新增
setn()
:12 票 - 新增
len[v]
:5 票
最近變更 · 偏好設定
編輯 · 歷程
上次編輯於 2019 年 3 月 8 日,下午 10:27 (GMT) (比對)