天天看點

Lua資料類型總結

一、類型與值

Lua是一種動态類型的語言。在語言中沒有類型定義的文法,每個值都“攜帶”了它自身的類型資訊。

在Lua中有8種基礎類型:

  • nil(空)
  • boolean(布爾)
  • number(數字)
  • string(字元串)
  • userdata(自定義類型)
  • function(函數)
  • thread(線程)
  • table(表)

1.1、nil(空)

nil是一種類型,它隻有一個值nil,它的主要功能是用于差別其它任何值。

一個全局變量在第一次指派前的預設值就是nil,将nil賦予一個全局變量等同于删除它。

Lua将nil用于表示一種“無效值”的情況,即沒有任何有效值的情況。

1.2、boolean(布爾)

boolean類型有兩個可選值:false和true。Lua與其它語言唯一的不同是,Lua中的任何值都可以表示一個條件,且隻有false和nil視為“假”,将除此之外的其它值視為“真”,例如字元串和數字0也都視為“真”。

1.3、number(數字)

number類型用于表示實數,書寫一個數字常量,可以使用普通的寫法,也可以使用科學計數法,例如:

4 、0.4 、4.57e-3 、0.3e12 、5e+20

1.4、string(字元串)

Lua中的字元串通常表示“一個字元序列”。Lua完全采用8位編碼,Lua字元串中的字元可以具有任何數值編碼,包括數值0。也就是說,可以将任意一個二進制資料存儲到一個字元串中。

Lua的字元串是不可變的值。不能像C語言中那樣直接修改字元串的某個字元,而是應該根據修改要求來建立一個新的的字元串,例如:

a = "one string"
b = string.gsub(a,"one","another") --修改字元串的一部分
print(a) --one string
print(b) --another string      

Lua的字元串和其它Lua對象(例如table或函數等)一樣,都是自動記憶體管理機制所管理的對象。這表示無須擔心字元串的配置設定和釋放,Lua處理的字元串可以小到隻包含一個字母,也可以大到包含整本書。Lua能夠高效地處理長字元串。Lua程式中操作100k或1M的字元串是很常見的。

字面字元串需要以一對比對的單引号或雙引号來界定:

a = "a line"
b = 'another line'      

Lua字元串中可以包含類似于C語言中的轉義序列,如下表示:

轉義字元 含義

​\a​

響鈴

​\b​

倒退

​\f​

提供表格

​\n​

換行

​\r​

回車

​\t​

水準tab

​\w​

垂直tab

​\\​

反斜杠

​\"​

雙引号

​\'​

單引号

還通過ASCII碼數值來指定字元串中的字元,例如:

"alo\n123\"" 等價于 "\97lo\10\04923"
a的ASCII碼值是97
\n的ASCII碼值是10
1的ASCI碼值是49      

Lua中使用​

​[[​

​​來建立換行字元串​

​]]​

​,例如:

page = [[
<html>
  <head>
    <title>An HTML Page</title>
  </head>

  <body>
    <a href="http://www.baidu.com">百度</a>
  </body>
</html>
]]      

如果字元串中有​

​]]​

​字元,例如"a=b[c[i]]",或者可能需要包含已經被注釋掉的代碼。為了應對這種情況,需要在兩個左方括号中間加上任意數量的等号,例如:

a = [==[
  b[c[i]]
]==]      

經過這樣修改後,字元串隻有在遇到一個内嵌相同數量等号的雙右括号時才會結束。如果一組左右方括号等号數量不等,那麼Lua就會忽略它。通過選擇适當數量的等号,就可以在不加轉義的情況下,直接嵌入任意的字元串内容了。

這套機制同樣适用于注釋,例如:以"–[=["開始的一個塊注釋将延伸至“]=]”結束。如此簡化了注釋那些“已經包含了注釋塊”的代碼,例如:

--[=[
print(a)
]=]      

Lua提供了運作時的數字和字元串的自動轉換。在一個字元串上應用算術操作時,Lua會嘗試将這個字元串轉換成一個數字,例如:

print("10" + 1) --11
print("10 + 1") --10 + 1
print("hello" + 1) --錯誤(不能轉換“hello”)      

Lua不僅在算術操作中會施以這種強制轉換,還會在其他任何需要數字的地方這麼做。相反,在Lua期望一個字元串但卻得到一個數字是,它也會将數字轉換成字元串,例如:

print(10 .. 20) --1020      

在Lua中,“…”是字元串連接配接操作符。當直接在一個數字後面輸入它的時候,必須要用一個空格來分隔它們,不然,Lua會将第一個點了解為一個小數點。

這種隐式轉換最好還是不要依賴它們,雖然在某些地方這些轉換顯得很便利,但它們也給語言和使用它們的程式帶來的複雜性。畢竟,字元串和數字是兩種不同的東西。比如運作10=="10"總是false,因為10是一個數字,而"10"是一個字元串。如果需要顯示地将一個字元串轉換成數字,可以使用函數​

​tonumber()​

​​。當這個字元串的内容不能表示一個正确的數字時,​

​tonumber()​

​将傳回nil。

line = io.read()
n = tonumber(line)
if n == nil then
    error(line .. "is not a valid number")
else
    print(n*2)
end      

若要将一個數字轉換成字元串,可以調用函數​

​tostring()​

​,或者将該數字與一個空字元串相連接配接:

print(tostring(10) == "10") -- true
print(10 .. "" == "10") -- true      

這樣的轉換永遠是合法的。

在Lua5.1中,可以在字元串前放置操作符"#"來獲得字元串的長度,例如:

a = "hello"
print(#a) -- 5
print(#"good\0bye") -- 8      

1.5、table(表)

table類型實作了“關聯數組”,關聯數組是一種具有特殊索引方式的數組。不僅可以通過整數來索引它,還可以使用字元串或其他類型的值(除了nil)來索引它。

table沒有固定的大小,可以動态地添加任意數量的元素到一個table中。

table是Lua中最主要的資料結構機制,具有強大的功能。基于table,可以以一種簡單、統一和高效的方式來表示普通數組、符号表、集合、記錄、隊列和其它資料結構。

Lua也是通過table表示子產品、包和對象的 。當輸入​

​io.read​

​​的時候,其含義是“io子產品中的read函數”。對于Lua而言,這表示“使用字元串"read”作為key(鍵)來索引​

​table.io​

​"。

在Lua中,table既不是“值”,也不是“變量”,而是“對象”。可以将一個table想象成一種動态配置設定的對象,程式僅持有一個對它們的引用(或指針),Lua不會暗中産生table的副本或建立新的table。

table的建立是通過“構造表達式”完成的,最簡單的構造表達式就是{},例如:

a = {}                -- 建立一個table,并将它的引用存儲到a
k = "x"               -- 建立一個字元串
a[k] = 10       -- 新條目,key="x",value=10
a[20] = "great"     -- 新條目,key=20,value="great"
print(a["x"])     -- 10
k = 20
print(a[k])       -- "great"
a["x"] = a["x"] + 1   --遞增條目"x"
print(a["x"])     -- 11      

table永遠是“匿名的”,一個持有table的變量與table自身之間沒有固定的關聯性。

a = {}
a["x"] = 10
b = a       -- b與a引用了同一個table
print(b["x"])   -- 10
b["x"] = 20     
print(a["x"])   -- 20
a = nil       -- 消除a對table的引用
b = nil       -- 消除b對table的引用      

當一個程式再也沒有對一個table的引用時,Lua的垃圾收集器最終會删除該table,并複用它的記憶體。

所有的table都可以用不同類型的索引來通路value,當需要容納新條目時,table會自動增長。

a = {}
-- 建立1000個新條目
for i=1,1000 do 
    a[i] = i*2
end
print(a[9])         -- 18
a["x"] = 10
print(a["x"])       -- 10
print(a["y"])       -- nil      

當table的某個元素沒有初始化時,它的内容就為nil。另外還可以像全局變量一樣,将nil賦予table的某個元素來删除該元素。

為了表示一條記錄,可以将字段名作為索引。Lua對于諸如a[“name”]的寫法提供了一種更簡便的“文法糖”,可以直接輸入a.name

a.x = 10    -- 等同于a["x"] = 10
print(a.x)    -- 等同于print(a["x"])
print(a.y)    -- 等同于print(a["y"])      

對于Lua來說,這兩種形式是等價的,可供自由使用。然而這兩種形式對于一個讀者來說,可能就暗示了不同的意圖。點的寫法可能更明确地暗示了讀者,将table作為一條記錄來使用,每條記錄都有一組固定的、預定義的key。而字元串的寫法可能暗示了該table會以任何字元串作為key,二現在出于某些原因,需要通路某個特定的key。

初學者常會将a.x和a[x]搞錯,前者表示a[“x”],表示以字元串“x”來索引table,而後者是以變量x的值來索引table,例如:

a = {}
x = "y"
a[x] = 10   -- 将10放入字段“y”
print(a[x])   -- 10
print(a.x)    -- nil,字段“x”的值未定義
print(a.y)    -- 10,字段“y”的值      

若要表示一個傳統的數組或線性表,隻需以整數作為key來使用table即可,直接初始化元素就可以了,例如:

-- 讀取10行内容,并存儲到一個table中
a = {}
for i=1,10 do
    a[i] = io.read()
end      

雖然可以用任何值作為一個table的索引,也可以用任何數字作為數組索引的起始值。但就Lua的習慣而言,數組通常以1作為索引的起始值。并且還有不少幾隻依賴于這個慣例。

在Lua 5.1中,長度操作符“#”用于傳回一個數組或線性表的最後一個索引值(或為其大小),例如:

-- 列印所有的行
for i=1,#a do
    print(a[i])
end      

以下是幾種長度操作符在Lua中的習慣寫法:

print(a[#a])        -- 列印清單a的最後一個值
a[#a] = nil         -- 删除最後一個值
a[#a+1] = v         -- 将V添加到清單末尾

-- 讀取一個檔案的前10行
a = {}
for i=1,10 do
    a[#a+1] = io.read()
end      

由于數組實際是一個table,是以關于其大小的概念可能會有些模糊。table中對于所有為初始化的元素的索引結果都是nil,Lua将nil作為界定數組結尾的标志。當一個數組有“空隙”時(即中間含有nil時),長度操作符會認為這些nil元素就是結尾标記。是以如果需要處理那些含有“空隙“的數組,使用長度操作符就不安全了,可以使用函數table.maxn,它将傳回table的最大正索引數。

由于可以用任何類型的值來索引table,是以可能會遇到一些看似相同,但卻實際不同的索引方式。例如:可以用數字0和字元串“0”來索引一個table,這兩個索引值是不同的,是以也就表示了table中兩個不同的條目。與此類似的還有字元串“+1”、“01”和“1”,這些都表示了不同的條目。當對索引的實際類型不是很确定時,可以明确地使用一個顯示轉換:

i = 10; j = "10"; k = "+10";
a = {}
a[i] = "one value"
a[j] = "another value"
a[k] = "yet another value"
print(a[j])         -- another value
print(a[k])         -- yet another value
print(a[tonumber(j)])   -- one value
print(a[tonumber(k)])   --one value      

1.6、function(函數)

在Lua中,函數是作為“第一類值”來看待的,這表示函數可以存儲在變量中,可以通過參數傳遞給其他函數,還可以作為其他函數的傳回值。

這種特性是語言具有極大的靈活性,為了給一個函數添加新的功能,程式可以沖的定義該函數。而在運作一些不受信任的代碼時,可以先删除某些函數,進而建立一個安全的運作環境。

此外,Lua對“函數式程式設計”也提供了良好的支援。例如,允許在某些詞法域中編寫嵌套的函數。

Lua既可以調用以自身Lua語言編寫的函數,又可以調用以C語言編寫的函數。Lua所有的标準庫都是用C語言寫的,标準庫中包括對字元串的操作、table的操作、I/O、作業系統的功能調用、數學函數和調試函數。同樣,應用程式也可以用C語言來定義其他函數。

1.7、userdata(自定義類型)

1.8、thread(線程)