日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

lua 5.3.5 使用pairs遍历table时, 遍历结果为什么是随机的

發(fā)布時間:2023/12/14 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 lua 5.3.5 使用pairs遍历table时, 遍历结果为什么是随机的 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • 1 遍歷結果是隨機的
  • 2 為什么會是隨機的
    • 2.1 簡單介紹table
      • 2.1.1 數(shù)組部分
      • 2.1.2 散列表部分
    • 2.2 獲取key在散列表中的位置
      • 2.2.1 首先介紹幾個宏
      • 2.2.2 獲取key在散列表中的位置
    • 2.3 key為字符串,獲取key(類型為字符串)在散列表中的位置
      • 2.3.1 TString結構體
      • 2.3.2 TString.hash的賦值
    • 2.4 計算G->seed
  • 3 總結

這里不討論lua中pairs和ipairs的區(qū)別。僅僅從lua源碼的角度,討論使用pairs遍歷table的時候,遍歷結果為什么是隨機的。

1 遍歷結果是隨機的

首先需要弄清楚,這個“遍歷結果是隨機的”是什么意思?

(1) 假設有 test.lua 文件如下,函數(shù)testPairs() 用來遍歷輸出table s:

function testPairs()s = {a=1,aa=2,b=3,c=4,ab=3,xx=4,ax=5,ay=4,xy=6}for k,v in pairs(s) doprint(k,v)end endtestPairs() print('-----------------------------') testPairs()

代碼中執(zhí)行了兩次 testPairs()函數(shù),那么輸出結果如何呢:

這不是一模一樣嗎,哪里隨機了?可能是table中元素不夠多,再多塞幾個元素到table中后,發(fā)現(xiàn)結果都是一樣的。

(2) 換種方式,更改一下"test.lua"中的代碼

function testPairs()s = {a=1,aa=2,b=3,c=4,ab=3,xx=4,ax=5,ay=4,xy=6}for k,v in pairs(s) doprint(k,v)end endtestPairs()

然后在命令行窗口中前后執(zhí)行兩次” lua test.lua”,結果如下:

這樣子做,遍歷結果的順序才是隨機的。此處通過表現(xiàn),先簡單下個結論。

使用pairs遍歷table時:
如果是同一次lua程序運行期間(同一個global_State下),遍歷結果的順序是不變的(畢竟table中的值都沒有變過);
否則,順序是隨機的。

2 為什么會是隨機的

2.1 簡單介紹table

本文僅涉及到table中的遍歷,只討論table中元素的存儲方式,所以不贅述table的相關操作(插入,刪除節(jié)點等操作)。

除了源碼,本文還參考了codedump的《lua設計與實現(xiàn)》(版本5.1.4)

table的定義如下:

與遍歷相關的字段為:

可以知道table是由數(shù)組和散列表兩個部分組成的。

2.1.1 數(shù)組部分

這部分比較簡單,只需知道對于array[i],key = i + 1, value = array[i]。
所以,key-value中的value存儲位置為:
(1) 數(shù)組中,當(key為正整數(shù) && 1<= key <= sizearray )
(2) 散列表中,其他值

2.1.2 散列表部分

根據(jù)key的類型和值,求得其對應的hash值,然后放到散列表中(當然可能會有沖突,處理沖突的方式可進一步閱讀函數(shù) luaH_newkey())

2.2 獲取key在散列表中的位置

那么將一個key-value插入到table的散列表中,其對應的位置是怎么計算的呢(此處不考慮有沖突的情況)。恰好,lua 源碼中將該功能提取成一個函數(shù):
Static Node *mainposition(const Table *t, const TValue *key)

2.2.1 首先介紹幾個宏

(1) sizenode() 求table的散列表大小

(2) lmod() 取模操作,這里的size其實就是(1)中的sizenode(t)

(3) gnode() 取table散列表中序號為i的元素的地址

(4) hashpow2(t, n) 根據(jù)n的值,對其進行l(wèi)mod操作,返回其在散列表中的位置

好的,這里記住hashpow2(t, n)的功能就可以了。

2.2.2 獲取key在散列表中的位置

這里代碼不多,直接上代碼

根據(jù)key的類型,選擇不同的計算方式:
簡單挑選幾個重要類型

(1) LUA_TNUMINT 整型


可以知道,針對整型,其實就是對其值求lmod操作。這說明如果key是整數(shù),其在散列表中的位置是不會變的(在前后兩次調用lua程序中,table的大小是不變的,array和node的大小也不會變)。

(2)LUA_TNUMFLT 浮點型

這里不展開l_hashfloat() 操作,只需知道,它是用來計算float的hash值即可

通過hashmod(t, n)可以知道,當key是浮點數(shù)的時候,其在散列表中的位置是不會變的。

(3) LUA_TSHRSTR 短字符串

這里不展開tsvalue(key),只需知道,它是用來將key從TValue類型轉換為TString類型(并不是普通的強制轉換,和TValue、GCUion結構有關)。

可以知道,針對短字符串,其實是對其hash值(TString.hash)求lmod操作。TString.hash值的獲取后面會詳細講。

(4) LUA_TLNGSTR 長字符串

可以知道,針對長字符串,其實是使用函數(shù) luaS_hashlongstr(TSring *)得到的值,并對其值求lmod操作。函數(shù) luaS_hashlongstr()后面會詳細講解。

根據(jù)以上四個類型可以知道,在散列表的長度不變(array和node長度不變)的情況下,對于一般類型(整數(shù),浮點數(shù),boolean等)的key, 其在散列表中的位置是不會變的。可以推測,如果在test.lua中,這樣設置table: t = {1,2,3, [100]=100, [200]=200},則其遍歷順序肯定是固定的,不是隨機的了。

所以這里面需要進一步探討的就是短字符串和長字符串了。

2.3 key為字符串,獲取key(類型為字符串)在散列表中的位置

2.3.1 TString結構體

先上TString結構,長、短字符串使用的都是這個結構體

只需要注意字段TString.hash,其值在求字符串在散列表中的位置時,起了至關重要的作用。

2.3.2 TString.hash的賦值

首先介紹一下函數(shù)luaS_hash(), 用來求一個字符串對應的hash值(這里暫時把函數(shù)luaS_hash(str, l, seed)的返回值叫做"字符串str的hash值")

其中str指向的是字符串,l是字符串的長度,seed是一個種子值。
通過函數(shù)luaS_hash()可知,只要種子不變,那么字符串對應的hash值當然也是不變的。

在new TString的時候,Tstring.hash值是在createstrobj()函數(shù)中進行賦值的,其值為h。

現(xiàn)在,需要找找是哪里調用了 createstrobj(),且參數(shù)h是多少?

(1) 新建字符串TString

(2) 短字符串
internshrstr()函數(shù)較長,這里折疊一部分無關代碼。

(lua源碼中經常喜歡用小寫字母的l,和數(shù)字1有點像,這里替換成size,表示字符串的長度)

可以知道,對于短字符串來說,TString.hash = luaS_hash(str, size, g->seed),其hash值和(str, size, G->seed)有關

(3) 長字符串

可以知道,對于長字符串來說, 初始的TString.hash = G->seed
現(xiàn)在我們回到2.2.2中求長字符串在散列表中的位置:


在函數(shù)createstrobj中可以知道,長字符串字段TString.extra的初始值為0,那么第一次求其散列表位置需要重新更新其TString.hash值,為:

luaS_hash(str, size, ts.hash)

而長字符串的hash字段的初始值也是G->seed。

與此可以知道,不管是長字符串還是短字符串,在求其在散列表中的位置時,都是使用luaS_hash(str, size, G->seed)的值進行l(wèi)mod操作后求得。

那么在兩次啟動lua程序過程中,字符串是沒有變化的(也就是str, size都是不變的),要想遍歷結果的順序是隨機的(對應散列表的位置是隨機的),那么只能是說G->seed是隨機的了。結果是這樣子嗎?

2.4 計算G->seed

直接上代碼
G->seed是在函數(shù)lua_newstate()中賦值的


(1) 四個addbuff()
makeseed()函數(shù)中連用了四個addbuff,其實就是將四個變量依次copy到char buff [4*sizeof(size_t)]中。

(2) Luai_makeseed()
這個比較簡單,其實就是調用C庫中的time()函數(shù),執(zhí)行time(NULL)得到的是當前的時間(unix時間戳,單位為秒),
這個值在每次啟動lua程序的時候,是不同的。

(3) luaS_hash(buff, p, h)
這個函數(shù)上面提到過。這里把當前Unix時間戳當做是seed,把buf字符串傳入,得到一個hash值。這個值就賦給當前G->seed

所以撇開buff[]字符串的值的隨機性不講,光是unix時間戳作為h傳入luaS_hash(buff, p, h)中,就知道,G->seed的值是隨機的。

這樣,再回到2.3.2 中計算字符串的TSTring.hash值(luaS_hash(str, size, G->seed))。

由此可以得出,先后兩次執(zhí)行l(wèi)ua程序,G->seed是隨機的,那么TString.hash當然也是隨機的,也就導致TString作為key時插入Table中,其對應的散列表的位置,也是隨機的。

3 總結

其實篇幅這么大,主要是回顧一下table相關的東西,解釋了前后兩次執(zhí)行l(wèi)ua程序,使用pairs遍歷相同的table時,為什么遍歷結果的順序是隨機的。

原因: 一言以蔽之,global_state.seed是一個和時間戳相關的隨機值,這個值會影響字符串的TString.hash字段,當TString*作為key插入table中時,這個字段會影響其在散列表中的位置。

總結

以上是生活随笔為你收集整理的lua 5.3.5 使用pairs遍历table时, 遍历结果为什么是随机的的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。