從 1 開始計算 |
|
Lua 核心確實 有一個 地方遵循這個原則:建構器的剖析,例如 {10,20,30}
。從 1 開始的慣例只由函式庫強制執行,而函式庫不是語言的一部分。--lhf
從 Lua 手冊
還有,讓我們直接處理這個問題:針對真實的表定義一個代理表,且索引中繼資料方法覆寫為在真實表中存取 i-1(如果 i 為正數)。決定是否直接將負數索引對應到真實表,或進一步處理它們。決定如何處理真實表中的索引 0。
完成後,在需要從 0 開始算的時候就使用代理表。在需要函式庫函數時就使用真實表。為了方便記憶,將您的表命名為『tablename1』,而代理表命名為『tablename0』。問題解決。
身為 C++ 和 Lua 程式設計師,我必須完全不同意這番咆哮。從 1 算起是「自然」的計算方式。在數學中,每個人都從 1 算起。從 0 算起只適用於 C 慣例,即陣列和指標是相等的,並且 a[i] 等於 *(a+i)。此外,我認為程式設計師可以輕鬆適應這種不像 C 的計算方式。但它讓 Lua 對「休閒程式設計師」更容易理解。--PeterPrade
+1
,因為 Lua 不使用半開範圍,但 C++(特別是在使用 STL 時,它整體上使用這種風格的範圍)時則不會發生這種情況。此外,我同意從 1 算起對人類來說是自然的(我從未表示過相反的意見),然而使用 [n, n-1]
來表示零長度範圍非常尷尬。告訴某人以地板上的某一點作為起點,然後再告訴他以落後一步的某一點作為終點,並不是傳達「不要往前走任何一步」的自然方式。相較之下,人類可以很輕易地理解「不要往前走任何一步」,方法是給他地板上的某一點作為起點,然後再給他與該點相同的點作為終點-- 相當於半開範圍 [n, n)
。--JohnBelmonte
半開区間を使用すると、簡潔なコーディングになる場合もあれば、そうでない場合もあります。私は、半開区間と閉区間、どちらでもフェンスポストエラーをたくさん見てきました(そして犯しました)。たとえば、C 配列の最後の要素のインデックスが配列の長さより 1 小さいという事実は、コード内にさまざまな種類の -1
が生じる可能性があります。これを排除するための手法があることは確かですが、私の Lua コードが +1
だらけで、私の C コードが -1
で乱雑であるとは私は言いません。ただし、半開区間については深刻な問題があります。範囲内にない数値を表す必要があることです。その結果、たとえば、長さ 256 の文字列のインデックスの型は、すべての有効なインデックスがバイトであっても、少なくとも short である必要があります。同様に、ベクトルの終了位置への半開区間記述子には、ベクトルの格納に含まれないアドレスが含まれており、他のオブジェクトへの有効なポインターである可能性があります。たとえば、これは、保守的ガベージコレクションでは複雑になります。(少なくとも 1 つの保守的ガベージコレクターは、この問題を補うために過剰割り当てを行います。)私はどちらか一方を支持しません。どちらも有効であり、どちらにも利点と欠点があります。 --RiciLake
與從零開始進行計算相同,我認為閉合區間比半開區間更直觀,至少在您不是在討論零長度區間的特殊情況時。我說「直觀」是指對至少沒有當過幾年程式設計師的人來說,這更自然。--PeterPrade
我沒有實際計算過,但我認為有絕大部分程式語言的計數都是從零開始的...由於我第一種學習的語言是 C,因此對我來說,從 1 開始算起很混淆,事實上,我用從 0 開始算起的陣列寫了一堆亂七八糟的 Lua 程式碼。--AdrianPerez
儘管從零開始計數有其優點,但我發現從 1 開始計數更自然,即使在寫程式時也是如此。對我來說,在 Lua 和 C 之間切換並不是個問題,因為過去我在 Visual Basic 和 C 之間也切換了很多次。--匿名
第一個是第 1 個,不是第 0 個。--Kazimir Majorinc
「我正在評估 Lua 以作為我應用程式中的嵌入式指令碼語言,到目前為止我所看到的一切都非常正面,直到我發現 Lua 是從索引 1 開始計算的。糟透了。糟透了。糟。糟糕到足以考慮把它全部丟出去。至於正當理由,即使不應該有任何理由:長度為 N 的陣列的索引是整數模 N 環的自然對應,有許多巧妙的特性。例如,當您想循環存取陣列時,只要進行 index = (index+1) %N
運算即可。從 1 開始的索引來做同樣的事情會很麻煩。此外,這會讓將 C 常式繫結到 Lua 變得很麻煩。」
iterator+1
才能存取表格索引。--DanHollis?
lua_createtable()
API 呼叫,允許您明確指定表格的陣列大小。您也可以指定雜湊大小,但那部分總是受限於 2 的次方。如果您建立一個具有特定陣列大小的表格,只要您沒有強制該表格擴充,其陣列部分就會準確地為那個大小,所以在您預先知道表格大小時,這會非常順利。對 int
等小型原子物件進行陣列繫結,在 5.1 中也變得稍微容易一些,因為有可能覆寫 #
運算子,但要讓 userdata
真正像一個 Lua 表格運作,您需要修改 ipairs
的預設定義,並可能修改 unpack
的預設定義。我在 Wiki 的 GeneralizedPairsAndIpairs 中放置了一些前者的程式碼,以防任何人需要使用它。--RiciLake
lua_Number
設定為將雙精度數值設為預設。同樣地,定義一個常數 lua_FirstIndex
(例如)並將其設為 1 作為預設,以參考第一個索引,或許會很有趣。與陣列第一個索引(例如,ipair
、foreachi
等)相關的 Lua 的每個部分都應該使用這個常數。如果使用者想要將這個常數變更為 0 並重新編譯,他/她可以自行承擔失去相容性的風險,使用從 0 開始的 Lua 表格。相同的常數應該可以從 Lua 存取,以允許指令碼具備可攜性或與索引陣列的第一個值無關。--Salvador Espana
{[0]=10, 20, 30}
,而表格函式庫函式的價值也會因此降低。--Ulrich Hoffmann <uho@xlerb.de>
[n,n)
,相鄰的範圍也能很好地加總 -- [a,b)+[b,c)=[a,c)
-- 而不必過度地在兩端加倍。產生的範圍長度為 (b-a)+(c-b)=c-a
- 長,其中任何一個或兩個組成都可以是長度為 0。另外,還能考慮 [m,n)
的「負範圍」,其中 m>n
,而範圍加總運算依然有效,現在甚至可以從非零長度的範圍中得到長度為零的範圍。
現在,考慮數字 a
、b
和 x
。一個正確的「x
在 a
和 b
之間」(在於 min(a,b)<=x<max(a,b)
)的表達(半開範圍)是 (a<=x)=(x<b)
。如果它是封閉範圍(...<=x<=...
),我們就無法有這麼漂亮的表達。-- Boyko Bantchev
作為一個指令碼語言,它的首要目標就是讓指令碼編寫者容易上手。一個有 5 個元素的陣列,其中最後一個元素的數字是 5,這樣的陣列肯定比最後一個元素是 4 的陣列,更容易向剛入門的指令碼編寫者解釋吧?
我在系統中介接了很多客戶端函式,好讓 Lua 可以呼叫這些函式。在很多情況下,從 0 開始或從 1 開始這個問題根本就無從適用。例如,很多函式都使用字串做為索引鍵(這樣一來問題就不存在了),不然就是根本沒有用到任何形式的陣列。
如果您希望 Lua 成為通用腳本語言,我絕對不建議使用編譯時間選項,以便基於零編寫的 Lua 腳本有一半可以執行,而基於 1 編寫的腳本也有一半可以執行。如果您這樣做,就會開啟噩夢的大門。我甚至看不出如果您想介接類似於 LuaSocket
、LuaCom
等函數,這會有什麼作用。這些函數的寫作會假設使用現有公約,也就是陣列從 1 開始,並且很多函數都提供針對 Windows 的預先編譯二進位檔案。如果您的系統是以零為基礎,這些函數可能完全無法執行,或者每個套件的作者都必須讓他們的程式碼因應各種不同的基礎而產生混亂,這肯定會讓變更基礎的任何優點都消失無蹤。-- NickGammon
double num = luaL_checknumber (L, 1); /* get first item on stack */
因此,向 C 介接 Lua 的程式設計師對此公約非常熟悉,您必須熟悉。如果將陣列改成從零開始,您也會跟著變更這個公約嗎?那您要如何從堆疊中取得最後一個元素(目前是 -1)?
上面有些文章並未明確提及是否也指的是字串。例如,字串中的第一個項目
c = string.sub ("ABC", 1) --> "A"
您是否也會把它改成零?如果是,您要如何取得最後一個項目?-- NickGammon
將 -1 用作最後一個元素的別名與半開區間完全一致(例如從零開始算起)。請看 Python。事實上,與封閉區間相比,它更為一致。想像一下清單會環繞,您可以從頭到尾,再從尾到頭(「捷徑」)移動。位置 0(清單開頭)的左邊一個位置是 -1(清單結尾)。換句話說,您只要從您的位置減去 1 就可以往左移動,這很自然。如果您清單從 1 開始,則會在清單的開頭和結尾產生兩個奇怪的位置差。當您要往左移動時,必須從您的位置減去一個,除非您處於位置 1,這時您必須減去 2。
對,這點沒錯。但由於「事情已經發生」,我建議,如果應用程式需要基於數學或其他原因從 0 開始計算,就簡單定義自己的 foreachi
函數,以變通目前行為。畢竟,目前沒有任何規定禁止你在資料表的 0 位置輸入元素。
如果你要讓資料表建構從 0 開始做這件事。
t = { [0] = "a", "b", "c" } -- a is in position 0, b is in 1 and so on
-- NickGammon
很不幸的是 #t 計算出來是 2。大多數問題來自於將數字與序數混淆。當索引是偏移量時,0 是陣列中第一個項目的偏移量。如果我們在口語中使用數字時更謹慎地保留型態資訊,這樣爭議會比較少。在數學中,將非負整數 n 視為從 0 到 n-1 的數字集合,會比將其視為從 1 到 n 的數字集合得到更簡潔的公式。-- GavinWraith
mtx[1][1]
。序列會比較多變[4]。關於數學軟體,Mathcad 預設是從 0 開始編號(但可以更改),而 Matlab、Maple 和 Mathematica 則是從 1 開始編號。--DavidManura
#t
(或 table.getn
)的定義並非回傳資料表中的元素數量。例如:
t = { foo = 1, bar = 2 } ; print (table.getn (t)) --> 0
從 Lua 手冊
我的範例與定義是一致的。#t
回傳最後一個項目的索引。
-- NickGammon
很顯然,我們無法就此達成共識,不過你覺得這樣不會造成 一點 混淆嗎?「下標 n…表示元素 n+1?」至少在 Lua 中,下標 n 表示元素 n。
-- NickGammon
個人淺見是,從 1 開始算很好。第一個元素是 1,最後一個元素是 -1。這樣就不必記住「這不是數學,這不是自然的事,習慣它」這樣的說法,就好像你在處理 Python 中的索引時一樣。
-- muntyan
而 Lua 讓您可以在整數中儲存 0 -- 所以它會帶您到哪個元素?
的確如此 -- JeanClaudeWippler
DIM arrayname(5 to 18)在 Forth 中,您也可以從任何數字開始,例如
HERE 14 ALLOT 5 - CONSTANT arrayname
看起來從一數起是來自一元制系統的古老遺產。我們應該在發明零的時候擺脫這個遺產,但很遺憾,我們沒有做到。
--Zifre
讓我們打個賭。我們將去公園並與隨機的人交談。我們將向他們展示一行中的三塊石頭,並請他們大聲數出石頭。如果有人說「零、一、二」,那麼你贏了。如果每個人都說「一、二、三」,那麼我贏了。我想我總是會贏得這個賭注。沒有人從零開始計數,因為這沒有道理。當以 0 訪問陣列的第一個元素時,該數字表示偏移量,而不是索引。問題不在於計數,而在於陣列應使用偏移量還是索引。
當然,使用偏移量而不是索引確實有一些優點。但很難與此爭論:如果陣列使用索引,那麼第「n」個元素則以數字「n」訪問。你需要第四個元素嗎?然後使用 4。想要第十個嗎?然後使用 10。這很有道理,並有其自己的一組好屬性。不幸的是,許多程式設計師對這個問題持封閉態度,並以某種方式說服自己相信荒謬的說法,例如「從 0 開始計數有更多的意義。」(然後,在他們自己的文章中,說明他們實際上從一個開始計數,就像這個星球上的其他人一樣。)
讓我們打第二個賭。在請人們數石頭後,我們會說:「請指出一塊石頭」。如果有人指著中間的石頭,那麼你贏得賭注。否則,我贏了。同樣,我看不出我如何才能輸掉這個賭注。-- 匿名
此外,這項說明 [a,b]+[b,c] 應為 [a,b]+[b+1,c] 的說法完全錯誤,因為原始主張需要半開式區間,而情況應為:[a,b)+[b,c)
因此,抗議完全錯誤,因為 [a,b)+[b,c) 並未包含 b 兩次。我將修正該部分,並刪除關於 [a,b]+[b+1,c] 的抱怨,因為這沒有意義。-- Tim
一些例外
string.gmatch
中, "%0"
是針對整個匹配,而 "%1%2"...
則針對第一、二次擷取。保持不變。
args
表的訂閱定義保持不變,但請注意,長度運算子現在將傳回一個大一的值。
math.random
是個不確定的點。目前狀況最適合實用性:提供一個引數時傳回 [0, n);提供兩個引數時傳回 [a, b]
for i=0,3
為 {0,1,2};for i=3,0,-1
為 {3,2,1}。
i==#t+1
(i==#t
十進位制) 已移除。我不明白這一點。現在會發生錯誤。其餘應該是微不足道的,包括一些負數索引作為反向支援的情況,在這些情況下,它將大多數像在 python 中那樣運作。
我反對採用十進位制表的原因還有另一點:從/轉換成 JSON(廣泛使用的資料交換格式)時會造成問題與混淆。
--farter