背景
Lua是C語言開發的腳本語言,設計的目的是為了嵌入到程序中,因此被設計得輕量小巧。Nginx配置中可以直接嵌入Lua 代碼或引入Lua 文件,Redis支持運行Lua語句和腳本,Wireshark中使用Lua腳本自定義協議。
本文用于收集常用的語法和API,用作記事本而不會過多深入,內容后續會不斷更新。
1.Lua數據類型和變量
Lua有8種基本類型:
[1] boolean布爾: true和false;在Lua中,僅false和nil表示假;其他為真,這與部分語言中默認值為假不同;
[2] number數值: 整數和浮點數;
[3] string字符串:由單引號或者雙引號包含的部分;
[4] function函數: 由C語言或者LUA定義的函數;
[5] table表:靈活的數據結構,可以用作數組、哈希表、集合等多種數據類型;
[6] nil: 空對象、空值、或者未聲明變量的值;
[7] userdata: C語言數據結構,lua不能直接訪問和操作這些數據,只能通過C語言的API來操作;
[8] thread類型: 在Lua中表示一個協程,用于實現并發。
lua作為動態語言,聲明變量時無需指定變量的類型,賦值決定類型,之后可通過type()函數查看變量的類型.
var1 = true
var2 = 100
var3 = 100.01
var4 = "test"
var5 = function(a)print(a)
endvar6 = {}
var7 = nilprint("type var1 is:" .. type(var1))
print("type var2 is:" .. type(var2))
print("type var3 is:" .. type(var3))
print("type var4 is:" .. type(var4))
print("type var5 is:" .. type(var5))
print("type var6 is:" .. type(var6))
print("type var7 is:" .. type(var7))
運行結果如下:
type var1 is:booleantype var2 is:numbertype var3 is:numbertype var4 is:stringtype var5 is:functiontype var6 is:tabletype var7 is:nil
變量有全局變量和局部變量之分,全局變量全局有效,而局部變量(在聲明時添加local)僅在所在語句塊內有效:
-- a為全局變量
>a=0-- b為局部變量
>local b='ss'
變量之間的比較,使用 == 表示相等, ~=表示不等:
>local a=1>print(a==1)
true>print(a~=1)
false
數值方面的大小比較以及基本運算與Java相同。
2.邏輯運算與條件判斷
使用 and or not 表示與或非:
>local b="ss">print(b=="s" or b=="ss")
true
Lua的if語法為:
if 條件 then邏輯
endif 條件 then邏輯1
else 邏輯2
endif 條件1 then邏輯1
elseif 條件2 then邏輯2
else邏輯3
end
3.字符串
Lua中定義字符串時,可用單引號或者雙引號。
local str = "hello world"
[1] 長度
-- string.len(arg)返回字符串長度
>local str = "hello world"
>print(string.len(str))
11
[2] 拼接
--使用..進行字符串拼接
>local str = "hello world"
>str_print = "str is : " .. str .. "."
>print(str_print)str is : hello world.
[3] 大小寫轉換
--全部轉為大寫string.upper(arg):
--全部轉為小寫string.lower(arg):>local str = "hello"
>strUpper = string.upper(str)
>print("strUpper: " .. strUpper)
strUpper: HELLO>strLower = string.lower(strUpper)
>print("strLower: " .. strLower)
strUpper: HELLO
[4] 是否包含
-- string.find(str, "test") 返回"test"在str中的其實索引,如果不存在,返回nil
>str = "test1"
>find_result = string.find(str, "test")
>print(find_result)
1--可通過是否等于nil來判斷是否包含
str = "test1"
if string.find(str, "test") ~= nil thenprint("yes")
elseprint("no")
end
[5] 截取
-- string.sub(str, i [, j]) 將str從i截取到j位,如果省略j表示截取到字符串尾部
>local str = "hello world"
>local str_sub1=string.sub(str, 1)
>print("str_sub1 is " .. str_sub1)
str_sub1 is hello world>local str_sub15=string.sub(str,1,5)
>print("str_sub15 is " .. str_sub15)
str_sub15 is hello
使用string.sub(str, i [, j]方法時注意Lua中位置索引從1開始而不是0開始,j是未知索引而不是長度,i和j都為閉區間。
[6] 刪除空格
function trim(inputString)return string.gsub(inputString, "^%s*(.-)%s*$", "%1")
end-- 示例用法:
local myString = " Hello, World! "
local trimmedString = trim(myString) -- 返回 "Hello, World!"
print(trimmedString) -- 輸出: Hello, World!
[7] 替換
--string.gsub(str, "test", "hello")
-- 將str字符串中的test改成hello
>local str = "test1test2test"
>local result = string.gsub(str, "test", "hello")
>print(result)
hello1hello2hello
4.table類型
table可以被當做數組或者哈希表使用。
數組:
-- 1.聲明方式:使用大括號
local myArray = {"a", "b", "c"}--2.獲取長度:通過#數組名,獲取數組長度
>print(#myArray)
3--3.添加元素
--通過table.insert(myArray, e) 添加元素
>local emptyTable = {}
>table.insert(emptyTable,"x")
>table.insert(emptyTable,"y")
>print(#emptyTable)
2
--也可以直接通過下標獲取、設置、添加元素
>emptyTable[1]="x1"
>print(emptyTable[1])
x1--4.刪除元素
table.remove(myArray) -- 刪除myArray數組最后一個元素
table.remove(myArray, i) --刪除myArray數組的第i個元素--5.遍歷
local myArray = {"a", "b", "c"}
for k, v in pairs(myArray) doprint(v) -- v為數組元素 a b c
end
其中local emptyTable = {}
聲明了一個數組,執行table.insert(emptyTable,"x")
向數組中添加了一個元素"x", 等價于emptyTable[1] ="x"
.
其他語言中,一般下標是下標,不會認為是整數類型的Key; 在Lua語言中,可以認為emptyTable[1] ="x"
是向哈希表emptyTable中添加了一個鍵值對,鍵是1,值是x.
哈希表:
--1.聲明哈希表,使用{}
local myHash = {name = "sy", role = "role_a"}--2.添加元素
myHash.age=18--3.刪除元素
myHash.age = nil--4.遍歷
local myHash = {name = "sy", role = "role_a"}
for k, v in pairs(myHash) doprint(k,v) -- k為鍵,v為值
end
注意:當哈希表中的鍵的值被設置為nil時,Lua在下一次垃圾回收時會清理這個鍵值對。
5.循環邏輯
while循環
while condition do-- 這里是循環體,只要condition為真,就會不斷執行這里的代碼
end--案例如下:
local i = 1
while i <= 5 doprint(i)i = i + 1
end
for循環
前面介紹table時已經用過for循環,除了遍歷數組和哈希表外,還可以指定循環次數:
-- initial 是起始值,limit 是結束值,step 是步長(步長可以是正數也可以是負數,默認為 1)
for var = initial, limit, step do-- 這里是循環體
end--案例如下:
local myArray = {"a", "b", "c"}
for i = 1, #myArray doprint(myArray[i])
end
注意:Lua有goto,但是沒有continue和break.
6.函數
和變量相同,Lua定義函數時可以通過local指定作用域: 全局或者局部。
使用關鍵字function聲明函數,模板如下所示:
function myFunName()--函數邏輯
end
(1) 函數入參
可以在聲明時添加參數
function myFunName(arg1,arg2,agr3)--函數邏輯
end--案例如下:
function printArgsFunc(arg1, arg2, arg3)print("args is: " .. arg1 .. arg2 .. arg3)
end
>printArgsFunc('a','b','c')
args is: abc
(2) 返回值
當函數需要返回數據時,通過添加return語句返回;和python相似,返回多個數據時,使用逗號分隔。
function myFunName(arg1,arg2,agr3)--函數邏輯return arg1,arg2,agr3
end--案例如下:
function getArgsFunc(arg1, arg2, arg3)return arg1, arg2, arg3
end
>local result1, result2, result3, result4 = getArgsFunc("a","b","c")
>print("result1 is: " .. result1)
>print("result2 is: " .. result2)
>print("result3 is: " .. result3)
>print(result4)result1 is: a
result2 is: b
result3 is: c
nil
由于Lua是腳本語言,因此需要先定義再調用,調用方式與python類似。
7.對象
Lua本身不具備面向對象的語法,但是可以使用面向對象的思想通過表數據結構模擬出對象。在介紹對象前,有必要提前說明一下元表的概念和一個Lua語法糖。
7.1 元表
元表的概念是定義原始值在特定下的行為。概念比較抽象和難懂,理解元表需要結合使用案例進行。
關聯方式
元表是一種特殊的表,每個表可以關聯一個元表,通過setmetatable(表,元表)
方法關聯:
t = {}
mt = {}
-- 表t關聯mt元表
setmetatable(t,mt)
__index屬性
當從表t中查詢不存在的key時,轉向元表mt的__index屬性。有兩種情況:
[1] __index為表:直接從__index表中根據key取值
>t = {}
>mt = { __index={ a=1, b=2 }}
>setmetatable(t,mt)
>print(t.a)
1
>print(t.c)
nil
分析: mt為t的元表,訪問t.a和t.c時,因為t表中不存在a和c屬性,請求會轉向元表的__index屬性,
此時__index屬性是元表且其中有a屬性—返回1,沒有c屬性—返回nil.
[2] __index為函數:將表t和key作為參數傳遞給__index函數
>t = {}
>mt = { __index=function(t, key)return "sy"end
}
>setmetatable(t,mt)
>print(t.a)
sy
>print(t.c)
sy
分析: mt為t的元表,訪問t.a和t.c時,因為t表中不存在a和c屬性,請求會轉向元表的__index屬性,
此時__index為方法,將t和key傳遞給__index方法,該方法返回固定值“sy”, 因此t.a和t.c得到的結果均為"sy".
__newindex屬性
當向表t中添加新的key時,轉向元表mt的__newindex屬性。有兩種情況:
[1] __newindex為表:直接添加到__newindex表中根據key取值
>t = {}
>mt = { __newindex={}
}
>setmetatable(t,mt)
>t.a="aa"
>print(t.a)
nil
>print(mt.__newindex.a)
aa
分析:mt為t的元表,向t中添加元素時,轉向元表mt的__newindex屬性,此時該屬性為表,將元素添加到__newindex中。
[2] __newindex為函數:將表t和key作為參數傳遞給__newindex函數
>t = {}
>mt = { __newindex=function(t,k,v)rawset(t,k,v)end
}
>setmetatable(t, mt)
>t.a="aa"
>print(t.a)
aa
分析:mt為t的元表,向t中添加元素時,轉向元表mt的__newindex屬性,此時該屬性為函數,將t,k,v作為參數傳遞給__newindex函數;其中rawset(t,k,v)函數將 k,v設置到t表中。
其他屬性:
__add用于重載表的操作符(+),類似的還有減法、乘除法等。
7.2 語法糖
Lua調用表中的方法時,如果需要將自己作為參數傳遞,可以使用冒號調用方法,從而省略第一個參數,如下所示:
table.func(table,arg1,argn)
-- 等價于
table:func(arg1,argn)
以下通過一個案例進行說明。
t = {name="sy",getName = function(t)return t.nameend
}
print(t.getName(t))
上述代碼定義了一個表t, 內部定義了一個name屬性和一個getName方法:getName方法接受一個表參數,并返回這個表的name屬性,此時獲取t的name屬性需要傳參t。
上面的方法可以修改一下,將t用self修改一下:
t = {name="sy",getName = function(self)return self.nameend
}
print(t.getName(t))
-- 等價于print(t:getName())
如此,使用getName方法的人,不會為getName傳遞其他表參數,認為這是特定為t表定制的方法。
print(t.getName(t))
可以使用語法糖print(t:getName())
代替。t:getName()
給人一種調用t對象的getName方法的感覺。
7.3 對象
定義一個Person表,只有一個name屬性和一個getName方法:
>Person={ name = "sy",getName = function(self)return self.name;end
}>print(Person:getName())
sy
為Person添加一個new方法:
function Person:new()local t = {}setmetatable(t,{__index=self})return t
end
通過Person的new方法可以創建一個新Person表,此時存在3個表:Person表,匿名表,t表(新Person表):
new方法中聲明了一個t表并在函數調用結束時返回t表,即調用new方法最終得到的新Person表是t表;
{__index=self}定義了一個匿名表;通過setmetatable方法將匿名表設置為t表的元表;
而匿名表的__index屬性為self,即Person表;
此時,當獲取 t表中不存在的屬性或者方法時,會轉向元表的__index屬性,即Person表。
因此,可通過新Person表訪問Person表的屬性和方法:
>local p = Person:new()
>print(p:getName())
sy
進一步地,可以為new方法添加一個元表參數,該元表將繼承Person表的屬性:
function Person:new(tableArg)local t = tableArg or {}setmetatable(t,{__index=self})return t
end
此時, 通過new方法得到的元表不僅擁有Person表的屬性,還保留tableArg表自身的屬性:
local Student = { class = 4 }
>local stuInstance = Person:new(Student)
>print(stuInstance.class)
4
>print(stuInstance:getName())
sy