這篇文章將會來一些比較輕松的內容,就是簡單的介紹一下Lua中幾個常用的庫。簡單的說就是幾個API的介紹。所以說,看起來比較容易,也沒有多大的分量。就是純粹的總結。使用庫就是為了方便我們的開發,提高開發效率,同時也能保證代碼的質量。希望大家以後也不要重復造輪子了。
數學庫
數學庫(math)由一組標准的數學函數構成。這裡主要介紹幾個常用的函數,其它的大家可以自行百度解決。
1.三角函數(sin,cos,tan……)
所有的三角函數都使用弧度單位,可以用函數deg(角度)和rad(弧度)來轉換角度和弧度。示例代碼:
print(math.sin(math.rad(30))) -- 0.5
謹記:三角函數的參數都是弧度,在實際使用中不要忘了,是弧度。
2.取整函數(floor,ceil)
floor:返回不大於x的最大整數;向下取整;
ceil:返回不小於x的最大整數;向上取整。示例代碼:
print(math.floor(5.6)) -- 5
print(math.ceil(5.6)) -- 6
3.最大值和最小值(max,min)
max:取參數中的最大值;
min:取參數中的最小值。示例代碼:
print(math.max(2, 3, 2, 14, 2, 30, -3)) -- 30
print(math.min(2, 3, 2, 14, 2, 30, -3)) -- -3
4.生成偽隨機數的函數(random,randomseed)
在實際開發中,生成隨機數的需求是經常有的。使用random和randomseed這兩個函數就可以輕易的完成。math.random用於生成偽隨機數,可以用3種方式來調用它:
(1)如果在調用時不提供任何參數,它將返回一個在區間[0, 1)內均勻分布的偽隨機實數;
(2)如果提供了一個整數n作為參數,它將返回一個在區間[1, n]內的偽隨機整數;
(3)如果提供了兩個整數參數m和n,它將返回一個在區間[m, n]內的偽隨機整數。
示例代碼如下:
print(math.random()) -- 輸出一個大於等於0,小於1的值
print(math.random(2)) -- 輸出不是1就是2
print(math.random(3, 4)) -- 輸出不是3就是4
如果你按照上面的代碼運行,然後再看著我寫的注釋,你可能就要罵我了,什麼破注釋了,明顯就是錯的麼?每次運行的輸出結果都是一樣的。是的,結果是一樣的,這就要說到即將出場的math.randomseed。函數math.randomseed用於設置偽隨機數生成器的種子數。(看到這裡,我姑且認為你是已經有過一年編程經驗的人員了,所以,你就不要問我什麼是種子了,這種概念性的東西,我想百度百科或者維基百科比我更有指導意義)math.randomseed的唯一參數就是一個我們稱為種子數的值。一般我們的做法是在一個程序啟動時,用一個固定的種子數來調用它,以此初始化偽隨機數生成器。那麼如何設置這個math.randomseed的種子值呢?如果使用同一個種子值的話,每次得到的隨機數就會是一樣的,在實際開發中,一般都是使用當前時間作為種子值,比如:
math.randomseed(os.time())
這樣就好了。一般在我們的程序啟動時,初始化一次種子就足夠了。我曾經傻傻的在一個循環中,使用math.random取隨機數,每次都調用math.randomseed(os.time())設置種子值(為什麼不可以?你可以自己試一試,看看結果。如果不懂,留下你的迷惑,我們繼續交流)。
math.randomseed(os.time())
print(math.random()) -- 輸出一個大於等於0,小於1的值
print(math.random(2)) -- 輸出不是1就是2
print(math.random(3, 4)) -- 輸出不是3就是4
這樣就好了,運行一下,看看結果吧。
table庫
table庫是由一些輔助函數構成的,這些函數將table作為數組來操作(重點:作為數組來操作的)。
1.插入和刪除函數
table.insert用於將一個元素插入到一個數組的指定位置,它會移動後續元素以空出空間。如果在調用table.insert時沒有指定位置參數,則會將元素添加到數組末尾。示例代碼:
local tb = {10, 20, 30}
table.insert(tb, 40) -- 在table的最後插入,結果為:{10, 20, 30, 40}
table.insert(tb, 2, 15) -- 在table的位置2插入,結果為:{10, 15, 20, 30, 40}
函數table.remove會刪除並返回數組指定位置上的元素,並將該位置之後的所有元素前移,以填補空缺。如果在調用這個函數時不指定位置參數,它就會刪除數組的最後一個元素。示例代碼:
local tb = {10, 20, 30}
print(table.remove(tb)) -- 刪除最後一個元素,並返回30;最後,tb = {10, 20}
print(table.remove(tb, 1)) -- 刪除第一個元素並返回10;最後,tb = {20}
現在有了這兩個操作,實現數據結構中的堆棧那就輕而易舉了。等什麼?自己試一試吧。
2.排序
對數組進行排序,這種需求,在實際開發中那是100%會遇到的。所以了,不會使用Lua對數組進行排序,那是會被別人笑掉大牙的。廢話少說。在Lua中,我們可以用table.sort完成這個任務。它可以對一個數組進行排序,還可以指定一個可選的次序函數。這個次序函數有兩個參數,如果希望第一個參數在排序結果中位於第二個參數值前,就應當返回true;如果沒有提供這個函數,table.sort就使用默認的小於操作。實例代碼:
local tb = {20, 10, 2, 3, 4, 89, 20, 33, 2, 3}
-- 默認是升序排序
table.sort(tb)
for _, v in ipairs(tb) do
print(v)
end
print("=======")
-- 修改為降序排序
table.sort(tb, function (a, b) if a > b then return true end end)
for _, v in ipairs(tb) do
print(v)
end
但是,在實際開發中,我們經常犯這樣的錯誤,總是試圖對一個table的索引進行排序。在table中,索引是一個無序的集合。如果對它們進行排序,則必須將它們復制到一個數組中,然後對這個數組進行排序。這就是我為什麼一開始強調的,table庫是對數組進行操作的。示例代碼:
local tb = {x = 20, z = 10, y = 30, m = 2, n = 8} -- 這是一個key無序的table
-- 如果想按照key的升序排列,下列代碼是不起作用的
table.sort(tb)
for k, v in pairs(tb) do
print(k .. " = " .. v)
end
正確做法是將這個table的所有key放入到一個數組中,對這個數組進行排序。示例代碼:
local tb = {x = 20, z = 10, y = 30, m = 2, n = 8} -- 這是一個key無序的table
local keyTb = {}
for k, _ in pairs(tb) do
keyTb[#keyTb + 1] = k
end
table.sort(keyTb)
for _, v in ipairs(keyTb) do
print(v .. " = " .. tb[v])
end
現在就是按照key的升序排列了。
3.連接
使用table.concat可以完成數組的連接。它接受一個字符串數組,並返回這些字符串連接後的結果,它有一個可選參數,用於指定插到字符串之間的分隔符,同時這個函數另外還接受兩個可選參數,用於指定第一個和最後一個要連接的字符串索引。示例代碼:
local tb = {"Jelly", "Think", "Is", "Good"}
local strTb = table.concat(tb, " ")
print(strTb)
字符串庫
重點來了,學習每種語言,在實際工作時,我們總是在和字符串進行打交道。Lua也不例外,在Lua中真正的字符串操作能力來源於字符串庫,字符串庫中的所有函數都導出在模塊string中。現在就來對string庫進行總結。
1.基礎字符串函數
直接通過代碼來說吧,示例代碼:
local str = "Jelly Think"
-- string.len可以獲得字符串的長度
local len = string.len(str)
print(len) -- 11
-- string.rep返回字符串重復n次的結果
str = "ab"
local newStr = string.rep(str, 2) -- 重復兩次
print(newStr) -- abab
-- string.lower將字符串大寫變成小寫形式,並返回一個改變以後的副本
str = "Jelly Think"
newStr = string.lower(str)
print(newStr) -- jelly think
-- string.upper將字符串小寫變成大寫形式,並返回一個改變以後的副本
newStr = string.upper(str)
print(newStr) -- JELLY THINK
這裡重點介紹一下string.sub(s, i, j)函數,它可以從字符串s中提取第i個到第j個字符。在Lua中,字符串的第一個字符的索引是1,但是,索引也可以是負數,表示從字符串的尾部開始計數,索引-1代表字符串的最後一個字符,以此類推。
local str = "[Jelly Think]"
local newStr = string.sub(str, 2, -2)
print(newStr) -- Jelly Think
newStr = string.sub(str, 2, 6)
print(newStr) -- Jelly
(重點:在Lua中,字符串和其它語言的一樣,是不可變的,以上的操作,都會返回一個新的值,但並不會修改原來的字符串。謹記,謹記!!!)
函數string.char和函數string.byte用於轉換字符及其內部數值表示;string.char函數接受零個或多個整數,並將每個整數轉換成對應的字符,然後返回一個由這些字符連接而成的字符串。string.byte(s, i)返回字符串s中第i個字符的內部數值表示,它的第二個參數是可選的,調用string.byte(s)可返回字符串s中第一個字符的內部數值表示。示例代碼:
print(string.char(97)) -- a
local i = 98
print(string.char(i, i + 1, i + 2)) -- bcd
print(string.byte("abc")) -- 97
print(string.byte("abc", 2)) -- 98
print(string.byte("abc", 2, 3)) -- 98 99
print(string.byte("abc", -1)) -- 99
在Lua中也有一個神奇的函數,string.format。和C語言中的printf是一致的。之所以說它神奇,因為它用的太多了,也太好用了。以至於我這裡就不多廢話了,我相信你們都會的。
2.模式匹配
由於模式匹配的東西比較多,所以,准備單獨寫篇博文單獨總結。
I/O庫
I/O庫為文件操作提供了兩種不同的模型,簡單模型和完整模型。簡單模型假設有一個當前輸入文件和一個當前輸出文件,它的I/O操作均作用於這些文件。完整模型則使用顯式地文件句柄。它采用了面向對象的風格,並將所有的操作定義為文件句柄上的方法。
1.簡單I/O模型
簡單模型的所有操作都作用於兩個當前文件。I/O庫將當前輸入文件初始化為進程標准輸入(stdin),將當前輸出文件初始化為進程標准輸出。在執行io.read()操作時,就會從標准輸入中讀取一行。
用函數io.input和io.output可以改變這兩個當前文件。io.input(filename)調用會以只讀模式打開指定的文件,並將其設定為當前輸入文件;除非再次調用io.input,否則所有的輸入都將來源於這個文件;在輸出方面,io.output也可以完成類似的工作。說完了input和output,在來聊聊io.write和io.read。