從0開始學(xué)lua,給自己記一個筆記,使用書籍 lua程序設(shè)計(第二版),電子工業(yè)大學(xué)出版社
此篇包含的內(nèi)容有
-
類型
-
表達(dá)式
-
語句
-
函數(shù)
下面開始正式內(nèi)容,有錯誤歡迎指出,小菜一個
1類型
-
table(表)
具有哈希結(jié)構(gòu)的關(guān)聯(lián)數(shù)組
可以通過除了nil外仍和類型的值來索引它,當(dāng)需要創(chuàng)建新條目(entry)時,table會自動增長
lua中主要的、僅有的數(shù)據(jù)結(jié)構(gòu)機(jī)制
lua中用table來表示模塊(module)、包(packge)、對象(object)例如 io.read 實(shí)際是 io模塊中的read函數(shù),通過key read在io表中進(jìn)行索引
lua中table既不是“值”也不是“變量”而是“對象”,可以當(dāng)做一種動態(tài)分配的對象,程序僅持有一個對他們的引用(||指針)lua不會暗中產(chǎn)生table的副本或者創(chuàng)建新的table
table永遠(yuǎn)是匿名的anonymous,一個持有table的變量與table自身沒有固定的關(guān)聯(lián)性
當(dāng)一個程序再也沒有對一個table的引用時,lua的垃圾收集器(garbage collector)最終會刪除該table并復(fù)用他的內(nèi)存當(dāng)table中的某個元素沒有被初始化時,它的內(nèi)容就為nil。通過將nil賦予table某個元素來刪除該元素,類似于全局變量。因?yàn)閘ua將全局變量存儲在一個普通的table中
數(shù)組通常你以1作為索引的起始值,并且有很多機(jī)制依賴于此慣例
長度操作符 # 用于返回一個數(shù)組或線性表的最后一個索引值(或?yàn)槠浯笮?) 如
for i=1 #a do
print(a[i])
end
- lua將nil作為界定數(shù)組結(jié)尾的標(biāo)志,當(dāng)一個數(shù)組有空隙(hole)即中間有值為nil時,長度操作符會認(rèn)為這些nil元素就是結(jié)尾。如果需要處理此類數(shù)據(jù),使用table.maxn,他會返回table的最大正索引數(shù)
-
userdata(自定義類型)
可以將任意C語言類型的數(shù)據(jù)存儲到lua變量中 lua中沒有太多的預(yù)定義操作 只能驚醒賦值和相等性測試
-
boolean(布爾)
true和false lua中的任何一個值都可以表示一個條件。不同于有些語言,lua中只有false和nil為假,其他任何值都為真
-
nil(空)
只有一個值,主要用于和其他任何值。一個全局變量在第一次賦值前的默認(rèn)值就是nil
-
thread(線程)
具體細(xì)節(jié)之后再講內(nèi)容較多
-
funtion(函數(shù))
lua中作為第一類值來看待,函數(shù)可以存儲在變量中 并可以當(dāng)做參數(shù)傳遞,(不同于c++,函數(shù)是第二類值,只能用過指向函數(shù)地址的函數(shù)指針來傳遞) 可以通過參數(shù)傳遞給其他函數(shù) 也可以作為其他函數(shù)的返回值
lua所有的標(biāo)準(zhǔn)庫都是用C語言寫的,標(biāo)準(zhǔn)庫中包括對字符串的操作、table的操作、I/O、操作系統(tǒng)的功能調(diào)用、數(shù)學(xué)函數(shù)和調(diào)試函數(shù)
-
string(字符串)
- 不能像c一樣直接修改字符串的值,每一次修改需要重新創(chuàng)建一個新的字符串
string a="11"; a+="2"; --錯誤
string a="11" a="112" --正確
string a="11".."2" --正確,lua中 .. 為字符串連接符
享元模式,相同字符串指向同一片內(nèi)存空間
有自動內(nèi)存管理機(jī)制所托管(gc)
轉(zhuǎn)義字符表
轉(zhuǎn)義字符:
\a 響鈴
\b 退格
\f 提供表格
\n 換行
\r 回車
\t 水平制表
\v 垂直制表
\\ 反斜杠
\" 雙引號
\' 單引號
-
number(數(shù)字)
用于表示實(shí)數(shù),lua中沒有整數(shù)類型 lua中的任何數(shù)字都可以表示任何32位整數(shù) 使用雙精度來表示一個 整數(shù)
可以使用科學(xué)計數(shù)法,如 0.4 4.37e6
2 表達(dá)式
-
算術(shù)操作符
二元:
+ 加法
- 減法
* 乘法
/ 除法
^ 指數(shù),右結(jié)合
% 取模
一元:
- 負(fù)號
--關(guān)于%運(yùn)算,規(guī)則如下:
a % b == a - floor(a/b) * b
--對于實(shí)數(shù)來說:
x%1 --> x的小數(shù)部分
x-x%1 --> x的整數(shù)部分
x-x%0.01 --> x精確到小數(shù)點(diǎn)后兩位
-
關(guān)系操作符
>
<
<=
>=
==
~=(!=)
對于table、userdata 和函數(shù),lua是做引用比較的。只有當(dāng)他們引用同一個對象是,才認(rèn)為相等
只能對于兩個數(shù)字或兩個字符串做大小型比較 對于 "0"與0,是不相同的
-
邏輯操作符
and ( && )
or ( || )
not ( ! )
- 與條件控制語句相同,所有的邏輯操作將false和nil視為假,而其他任何東西都為真。
- 對于 and 和 or 來說,都是用 短路求值,也就是說他們只會在需要時哦按段第二個操作數(shù)(如and前為假,lua變不判斷之后)
常用操作:
x=x or v (若x為空賦默認(rèn)值v)
max=(x>y)and x or y (返回最大值)
-
字符串連接
- ".." (字符串連接符) "hello ".."world" -->"hello world"
- 在操作中如果任意一個操作室是數(shù)字,lua會將其轉(zhuǎn)換成一個字符串
- lua中字符串是不可變值,連接操作只會創(chuàng)建 一個新的字符串
- 右結(jié)合
-
table構(gòu)造式
- 用于創(chuàng)建和初始化table
- 最簡單的構(gòu)造式為{}空構(gòu)造式,創(chuàng)建一個空的table
初始化數(shù)組:
words={"a","b","c","d","e"} printa(a[3]) --> c
a={x=10,y=20} ==> a=[]; a.x=10; a.y=20;
構(gòu)造式{x=0,y=0} ==> {["x"]=0,["y"]=0}
--在一個構(gòu)造式中,除了逗號還可以用分號。一般用分號用于分隔構(gòu)造式中不同的成分,例如
{x=10,y=45;"one","two","three"}
3.語句
-
賦值
普通賦值 與c++相似差異在于多重賦值
lua 中允許多重賦值,例如 a,b = 10 ,2x 賦值后,a為10,b為2x
在多重賦值中,lua先對等號右邊所有元素求知,然后才執(zhí)行賦值 如 x,y=y,x 交換x與y的值
lua總會將有編制的個數(shù)調(diào)整到與左邊變量的個數(shù)一致。 規(guī)則是 若右邊值數(shù)量少,則多語的變量會被賦值為nil ;若右邊值多,則多于的值會被丟棄
注意: a,b,c=0 賦值結(jié)果為 0 nil nil多重賦值一般用于交換兩個變量或接收函數(shù)的多個返回值(lua中函數(shù)允許返回多個返回值)
-
局部變量與塊
通過local語句來創(chuàng)建局部變量
局部變量的作用域僅限于生命他們的那個塊。一個塊(block)是一個控制結(jié)構(gòu)的執(zhí)行體,或者是一個函數(shù)的執(zhí)行體再或是一個程序塊
x=10
local i =1 -- 程序塊中的局部變量
while i<=x do
local x=i*2 --while循環(huán)中的局部變量
print(x)
end
if i>20 then
local x -- 新局變量x
x=20
print(x+2) --成功輸出22,局部x
else
print(x) --成功輸出10,全局
end
print(x) --輸出10 全局
--如果是交互模式,可能不是此結(jié)果。
--交互模式中 每行輸入的內(nèi)容自身就形成了一個程序塊
--避免此情況需要使用關(guān)鍵字 do-end
do
local a2=2*a
local d=(b^2-4*a*c)^{1/2}
x1=(-b+d)/a2
x2=(-b-d)/a2
end --a2和d的作用域至此結(jié)束
print(x1,x2)
在編程中盡可能的使用局部變量,這是一種良好的編程風(fēng)格 可避免一些無用的名稱引入全局環(huán)境,而且訪問局部變量比訪問全局變量更快 切在作用域結(jié)束時,便會消失 垃圾回收器便可釋放該值
在lua中有一種習(xí)慣寫法時
local foo=foo
這句代碼創(chuàng)建了一個局部變量foo,并用全局變量foo的值初始化它,如果后續(xù)其它函數(shù)改變了全局foo的值,那么可以在這里先將他的值保存起來。這種方法還可以加速在當(dāng)前作用域中對foo的訪問
- 在需要時才聲明局部變量,使得其在初始化時就擁有一個有意義的初值,并且縮短變量的作用域 這樣有助于提高代碼的可讀性
-
控制結(jié)構(gòu)
1. 語法
if then else
if a<0 then a=0 end
if a<b then return a else return b end
if line>MAXLINES then
showpage()
line=0
end
-- =============================
多個嵌套
if a==1 then
a=2
elseif a==2 then
a=3
else a=4
end
-- =============================
while
local i=1
while a[i] do
print(a[i])
i=i+1
end
repeat 一條repeat-until 語句重復(fù)執(zhí)行其循環(huán)知道條件為真是結(jié)束
--打印一行不為空的內(nèi)容
repeat
local num=io.read()
until num>0
2. for循環(huán)
2.1 數(shù)字型for
for var=exp1,exp2,exp3 do
<執(zhí)行體>
end
--表示var從exp1變化到exp2,每次以exp3為步長(默認(rèn)為1)
for i=1,10 do print(i) end --正序輸出1-10(包含10)
for i=10,1,-1,do print(i) end --倒敘輸出1-10(包含1)
不要在循環(huán)過程中修改控制變量(i)的值,跳出直接break
2.2 泛型for(foreach)
泛型for循環(huán)通過一個迭代器(iterator)函數(shù)來遍歷所有值
for i ,v in ipairs(a) do print(v) end --打印數(shù)組a的所有值
Lua基礎(chǔ)庫 提供了ipairs,這是一個用于遍歷數(shù)組的迭代器函數(shù)。在每次循環(huán)中,i會被賦予一個新的索引值,同事c被賦予一個對應(yīng)于該索引的數(shù)組元素值
標(biāo)準(zhǔn)庫提供的幾種迭代器:
io.lines --迭代文件中的每行
pairs --迭代table元素
ipairs --迭代數(shù)組元素
string.gmatch --迭代字符串中帶刺
同時,也可以自行編寫迭代器
3. break/return
與其他語言基本相同,不同是break與return 只能是一個舉哀的最后一條語句,或者是end else until前的一條語句
4.函數(shù)
-
基本屬性
lua中函數(shù)可沒有返回值(return 語句)此時返回的是nil
function testPrint()
print "test print"
end
local t=testPrint()
print(type(t)) -- nil
正常情況后面必須跟隨圓括號,除以下特例:一個函數(shù)只有一種參數(shù),且改參數(shù)是字面字符串或table構(gòu)造式
print "hello world" ==>print("hello world")
dofile 'a.lua' <==> dofile('a.lua')
為面向?qū)ο笫降恼{(diào)用提供了一種特殊語法: 冒號操作符 例如:
o.foo(o,x) ==> o:foo(x) --隱含將o作為函數(shù)的第一個參數(shù)
lua中形參數(shù)量與實(shí)參數(shù)量可以不同,會自動調(diào)整數(shù)量(規(guī)則和多重賦值相同)
function f(a,b) return a or b end
f(3)a=3,b=nil
f(3,4) a=3,b=4
f(3,4,5) a=3,b=4 5被舍棄
-
多重返回值
lua具有一項(xiàng)與其他語言不同的特征,允許函數(shù)返回多個結(jié)果 例如
s,e=string.find("hello lua user","lua") --如果找到返回匹配的起始字符和結(jié)尾字符的索引
編寫具有多返回值的函數(shù):
function testReturn(n)
if n>0 then
return 1,2
else
return 3
end
end -- n>0返回1,2 否則返回3
lua會調(diào)整一個函數(shù)的返回值類型數(shù)量以適應(yīng)不同的調(diào)用情況,如下。
- 若將函數(shù)調(diào)用作為一條單獨(dú)語句時,lua會丟棄所有的返回值
- 若將函數(shù)作為表達(dá)式的一部分調(diào)用時,lua只保留函數(shù)的第一個返回值
- 當(dāng)函數(shù)是一系列表達(dá)式中的最后一個元素時,才能獲得 他的所有返回值(多重賦值,函數(shù)調(diào)用時傳入的參數(shù)列表。table的構(gòu)造式、return語句)
function foo() return 1 ,2 end
x,y=foo() --x=1 y=2
x,y=foo(),3 --x=1 y=3
- 當(dāng)一個函數(shù)調(diào)用作為另一個函數(shù)調(diào)用的最后一個實(shí)參時,第一個函數(shù)的所有返回值豆?jié){作為實(shí)參傳入第二個參數(shù)
print(foo()) -- 1 2
print(foo(),3) --1 3
- 當(dāng)foo2出現(xiàn)在一個表達(dá)式中時,lua會將其返回值數(shù)量調(diào)整為1 如
print(foo().."x") --1x
- table表達(dá)式可以完整的接收一個函數(shù)調(diào)用的所有結(jié)果(只有當(dāng)一個函數(shù)調(diào)用作為最后一個元素時發(fā)生)
t={foo()} --t[1]=1 t[2]=2
t={foo(),3} --t[1]=1,t[2]=3
t={0,foo()} --t[1]=0 t[2]=1 t[3]=2
- 也可以將一個函數(shù)調(diào)用放入一對圓括號中,從而只返回一個結(jié)果如
print((foo())) --輸出1
- 關(guān)于多重返回還有一個特殊函數(shù) upack 他接受一個數(shù)組作為參數(shù),并從1開始返回所有元素
print(unpack ({10,20,30}) ) --10 20 30
a,b=unpack(10,20,30) --a=10,b=20 30被丟棄
- unpack的一項(xiàng)重要用途體現(xiàn)在泛型調(diào)用(generic call) 機(jī)制中 ASNI C 中(c的標(biāo)準(zhǔn))是無法編寫泛型調(diào)用的代碼 最多是聲明一個可以接受長參數(shù)的函數(shù) 或者通過一個函數(shù)指針來調(diào)用不同的函數(shù) 并且無法在同一次函數(shù)調(diào)用中傳入動態(tài)數(shù)量的參數(shù),每次在調(diào)用時必須傳入固定數(shù)量的參數(shù) 并且每個都要具有確定的類型 而lua中可以做到
unpack函數(shù)等價于 return list[1],list[2].....list[j] (j=#list)
-
變長參數(shù)
lua中的函數(shù)還可以接受不同數(shù)量的實(shí)參,例如
function Add(...)
local s=0
for i,v in ipairs{...} do
s=s+v
end
return s
end
print(add(3,4,10,25,12)) --> 54
參數(shù)中的3個點(diǎn) (...) 表示該函數(shù)可接受不同數(shù)量的實(shí)參,一個函數(shù)要訪問它的變長參數(shù)時,仍需用到三個點(diǎn)(...)此時這三個點(diǎn)是作為一個表達(dá)式來使用的 表達(dá)式{...}表示一個由所有變長參數(shù)構(gòu)成的數(shù)組
表達(dá)式 "..." 的行為類似于一個具有多重返回值的函數(shù),他返回的是當(dāng)前函數(shù)的所有變長參數(shù)
具有變長參數(shù)的函數(shù)同樣也可以擁有任意數(shù)量的固定參數(shù),但固定參數(shù)必須放在變長參數(shù)之前。
在遍歷一個變長參數(shù)時通常只需用到{...]如同訪問一個table一樣。然而如果變長參數(shù)中有我們刻意傳入的nil值,則需 用到函數(shù)select,規(guī)則如下
如果 index 是個數(shù)字, 那么返回參數(shù)中第 index 個之后的部分; 負(fù)的數(shù)字會從后向前索引(-1 指最后一個參數(shù))。 否則,index 必須是字符串 "#", 此時 select 返回參數(shù)的個數(shù)。如
for i=1,select('#',...) do
print(select('i',...))
end
-
具名實(shí)參
類似于c/c++中具有默認(rèn)值的參數(shù) int GetAge(int age=0) 但lua中不支持這種語法,我們可以通過一些操作達(dá)到相同的效果,如下示例
function Window(options)
--檢查必要參數(shù)
if type(options.title)~="string" then error("no title") end
if type(options.width)~=“number" then error("no width") end
if type(options.height)~= "number" then error("no height") end
--其他參數(shù)都是可選的
_Window(options.title, -- _Window 真正的創(chuàng)建窗口函數(shù)
options.x or 0 --默認(rèn)值
options.y or 0 --默認(rèn)值
options.width,options.height
options.background or "white" --默認(rèn)值
)
end