程式設計小知識之 Lua 長度運算子

2021-10-01 14:52:03 字數 3213 閱讀 2850

本文講解了 lua 中長度運算子(#)的一些知識

(注: 以下討論基於 lua 5.3.5 版本)

基礎

lua 中的長度運算子(#)可以用於獲取 table 的"長度",舉個簡單的例子:

local t = 

print(#t) -- 3

但其實對於 table 而言,長度運算子並不等同於獲取 table 的"長度",更準確一些的說法應該是獲取 table序列部分的長度,而所謂序列,是指索引為 1 至 n 的集合(中間不能有空元素),以上面的**為例,表(table) t 就是乙個序列, 索引為 1 至 3,所以表(table) t 的長度即為 3.

而對於下面的 表(table) t:

local t =
雖然表(table) t 有 4 個元素(索引為 1 至 4),但是索引 4 為空元素(nil),所以表(table) t 的序列部分索引是 1 至 3,所以表(table) t 的長度仍為 3:

local t = 

print(#t) -- 3

在實際開發中,也並不建議在用作序列的 table 中插入空元素(nil),所以一般來講,能夠在用作序列的 table 上正確使用長度運算子(#),並且了解長度運算子(#)的侷限性(只能正確作用於序列上)就足夠了.

以下內容涉及實現細節,討論的示例也並不常見,僅想初步了解的朋友可以跳過閱讀,否則容易引起混淆

高階

接著上面的例子,我們再來看下這段**:

local t = 

print(#t) -- ?

按照之前的理解,似乎輸出應為 2(因為表(table) t 的序列部分索引為 1 至 2),但實際上,程式的輸出為 4:

local t = 

print(#t) -- 4

原因在於 lua 的相關實現中,長度是從最大的陣列索引處開始查詢的,如果發現該處的元素不為空(nil),就直接向後查詢.

在上面的例子中, lua 首先檢查 t[4](t 的最大陣列索引為 4),發現不是空元素,於是直接向後查詢,發現不存在 t[5] 元素,於是便返回了 4(作為 table 的序列長度,下同).

我們接著來,考慮下面**:

local t = 

print(#t) -- ?

按照之前的講解,現在表(table) t 的最大陣列索引處(t[6])為空元素,於是我們應該直接向前查詢 t[5],然後發現 t[5] 並不是空元素,於是返回 5.

但實際上,程式的輸出為 2:

local t = 

print(#t) -- 2

原因在於當 lua 發現 table 最大陣列索引處的元素為空時,是按二分法的方式向前查詢的,當發現 t[6] 為空元素之後, lua 向前查詢的元素不是 t[5],而是 t[3],接著發現 t[3] 是空元素,於是從 t[3] 開始繼續向前二分查詢,最後返回了 2.

接著我們可以來做些練習了:

local t = 

print(#t) -- ?

按照上面的解釋,我們很容易知道輸出應為 1:

local t = 

print(#t) -- 1

接著我們進行賦值操作:

local t = 

print(#t) -- 1

t[8] = 1

print(#t) -- ?

這時 table 的最大陣列索引處(t[8])不為空元素,按照先前的解釋,輸出會變成 8:

local t = 

print(#t) -- 1

t[8] = 1

print(#t) -- 8

我們再進行一次賦值操作:

local t = 

print(#t) -- 1

t[8] = 1

print(#t) -- 8

t[9] = 1

print(#t) -- ?

這個時候輸出為多少呢?你也許會猜測是 9,但實際上輸出為 1 !

local t = 

print(#t) -- 1

t[8] = 1

print(#t) -- 8

t[9] = 1

print(#t) -- 1

原因在於我們最後一次的賦值操作因為新建了索引(之前不存在索引 9),繼而觸發了 table 的 rehash 流程,在這個流程中, lua 會根據 table 元素的分布重新調整陣列的大小,使的最後的輸出變為了 1(這裡我們不展開 rehash 的流程細節,有興趣深入的朋友可以看看 lua 原始碼中的 rehash 函式).

高階如果混合使用 table 中的 陣列部分 和 hash部分,則長度運算子(#)的結果會更加複雜一些:

local t = 

print(#t) -- ?

當 lua 發現 table 的最大陣列索引處不為空元素時,其會繼續在 table 的 hash部分 尋找,繼而導致上面的輸出為 5:

local t = 

print(#t) -- 5

另外, hash 部分的查詢流程也是二分進行的,這也導致以下**的輸出為 10(而上面**的輸出為 5) :

local t = 

print(#t) -- 10

最後乙個例子有些隱晦,在此我們僅僅列出結果,有興趣了解原因的朋友可以看看 lua 原始碼中的 luao_int2fb 和 luao_fb2int 兩個函式:

local t = 

print(#t) -- 18

local t =

print(#t) -- 1

總結

了解 lua 中長度運算子(#)的作用並不困難,但其中涉及的細節並不簡單(有時候甚至有些隱晦),有興趣深入的朋友可以從 lua 原始碼中的 luah_getn 函式開始探索.

移位運算子小知識

移位運算子它主要包括 左移運算子 無符號右移運算子 有符號右移運算子 移位運算子操作的物件就是二進位制的位,可以單獨用移位運算子來處理int型整數。左移運算子用 表示,是將運算子左邊的物件,向左移動運算子右邊指定的位數,並且在低位補0。其實,向左移n位,就相當乘上2的n次方。無符號右移運算子用 表示...

程式設計小知識之 自增 自減 運算子

本文講述了 c c 中 自增 自減 運算子 的一些知識 自增 自減 運算子應該是 c c 程式設計中的基礎知識了,而自增 自減 運算子又有兩種形式,分別是前置自增 自減 和後置自增 自減 出於簡單考慮,後文僅以自增運算子進行舉例講解 譬如表示式 i 和 i 就分別表示對 i 進行前置自增 和 對 i...

lua 範例 Lua 運算子

lua 運算子 運算子是乙個特殊的符號,用於告訴直譯器執行特定的數學或邏輯運算。lua提供了以下幾種運算子型別 算術運算子 關係運算子 邏輯運算子 其他運算子 算術運算子 下表列出了 lua 語言中的常用算術運算子,設定 a 的值為10,b 的值為 20 操作符描述例項 加法a b 輸出結果 30 ...