一、類型與值
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語言中的轉義序列,如下表示:
轉義字元 | 含義 |
| 響鈴 |
| 倒退 |
| 提供表格 |
| 換行 |
| 回車 |
| 水準tab |
| 垂直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語言來定義其他函數。