1.Lua 交互式編程 and 腳本式編程
2.注釋
單行:--
多行:--[[ --]] 多行推薦使用: --[=[ ]=]
3.變量
變量默認(rèn)是全局的且不需要聲明,直接使用不會(huì)報(bào)錯(cuò)(為nil);刪除變量可賦值為nil
4.Lua 中有 8 個(gè)基本類型分別為:
nil揭芍、boolean绒怨、number、string、table 、function、userdata滑蚯、thread
nil :類型表示一種沒有任何有效值 ;對(duì)于全局變量和 table抵栈,nil 還有一個(gè)"刪除"作用 告材。
boolean:Lua 把 false 和 nil 看作是"假",其他的都為"真" 竭讳;數(shù)字0也表示true创葡。
number類型 -- double(雙精度)類型 浙踢。
String:由一對(duì)雙引號(hào)或單引號(hào)來表示 ,"[[]]" 來表示"一塊"字符串 绢慢。純數(shù)字字符串上進(jìn)行算術(shù)操作時(shí),Lua 會(huì)嘗試將這個(gè)數(shù)字字符串轉(zhuǎn)成一個(gè)數(shù)字 ;字符串連接使用的是: .. 胰舆; 使用 # 來計(jì)算字符串的長度骚露,放在字符串前面。
Lua 中的表(table)其實(shí)是一個(gè)"關(guān)聯(lián)數(shù)組"(associative arrays)缚窿,數(shù)組的索引可以是數(shù)字或者是字符串棘幸。 Lua 里表的默認(rèn)初始索引一般以 1 開始 ;table 的索引使用方括號(hào)[]倦零。Lua 也提供了 . 操作 误续。
userdata :是一種用戶自定義數(shù)據(jù),用于表示一種由應(yīng)用程序或 C/C++ 語言庫所創(chuàng)建的類型扫茅。
在 Lua 里蹋嵌,最主要的線程是協(xié)同程序(coroutine)。它跟線程(thread)差不多葫隙,擁有自己獨(dú)立的棧栽烂、局部變量和指令指針,可以跟其他協(xié)同程序共享全局變量和其他大部分東西
5.變量
Lua 中的變量全是全局變量恋脚,那怕是語句塊或是函數(shù)里腺办,除非用 local 顯式聲明為局部變量
Lua 可以對(duì)多個(gè)變量同時(shí)賦值,變量列表和值列表的各個(gè)元素用逗號(hào)分開 糟描。
當(dāng)變量個(gè)數(shù)和值的個(gè)數(shù)不一致時(shí)怀喉,Lua會(huì)一直以變量個(gè)數(shù)為基礎(chǔ)采取以下策略:
a. 變量個(gè)數(shù) > 值的個(gè)數(shù) 按變量個(gè)數(shù)補(bǔ)足nil
b. 變量個(gè)數(shù) < 值的個(gè)數(shù) 多余的值會(huì)被忽略
應(yīng)該盡可能的使用局部變量,有兩個(gè)好處:避免沖突和快
6.流程控制
while ( boolean表達(dá)式) do statements end
repeat statements until(boolean表達(dá)式)
for
pairs 能迭代所有鍵值對(duì)船响。
ipairs 可以想象成 int+pairs磺送,只會(huì)迭代鍵為數(shù)字的鍵值對(duì)
for 循環(huán)中,循環(huán)的索引 i 為外部索引(不是全局變量),修改循環(huán)語句中的內(nèi)部索引 i,不會(huì)影響循環(huán)次數(shù):
數(shù)值for:var 從 exp1 變化到 exp2凑阶,每次變化以 exp3 為步長遞增 var泪掀,并執(zhí)行一次 "執(zhí)行體"。exp3 是可選的
i=2;
for i=1,10 do
--i = 10 --區(qū)別
print("one time,i:"..i)
print(_G.i);--全局變量i
end
print(i) -- nil
泛型for
for k,v in pairs(_G) do
print(k..":"..type(v))
end
任然循環(huán)10次诵竭,只是i值被修改了(都是10),結(jié)束循環(huán)再次print是nil
有break沒有continue;
if(布爾表達(dá)式1)
then
--[ 在布爾表達(dá)式 1 為 true 時(shí)執(zhí)行該語句塊 --]
elseif( 布爾表達(dá)式 2)
then
--[ 在布爾表達(dá)式 2 為 true 時(shí)執(zhí)行該語句塊 --]
elseif( 布爾表達(dá)式 3)
then
--[ 在布爾表達(dá)式 3 為 true 時(shí)執(zhí)行該語句塊 --]
else
--[ 如果以上布爾表達(dá)式都不為 true 則執(zhí)行該語句塊 --]
end
7.函數(shù)
Lua 提供了許多的內(nèi)建函數(shù) ;可以遍歷_G查看
[local] function function_name( argument1, argument2..., argumentn)
function_body
return result_params
end
函數(shù)可以接受可變數(shù)目的參數(shù)汗销,和 C 語言類似,在函數(shù)參數(shù)列表中使用三點(diǎn) ... 表示函數(shù)有可變的參數(shù) ,可變參數(shù)放在最后抵窒,(local args ={...} ;select("#", ...)返回可變參數(shù)個(gè)數(shù))
8.運(yùn)算符
^ :冪運(yùn)算
~= :不等于
and or not
優(yōu)先級(jí)
^
not - (unary)
/
-
..
< > <= >= ~= ==
and
or
9.字符串操作
sub:function
upper:function:字符串全部轉(zhuǎn)大寫 string.upper(arg)
len:function:計(jì)算字符串長度
gfind:function
rep:function string.rep(string, n) 返回string的n個(gè)拷貝串
find:function:string.find (str, substr, [init, [end]]) 在目標(biāo)字符串str中搜索指定的內(nèi)容substr(第三個(gè)參數(shù)為索引),返回其具體位置弛针。不存在則返回 nil
match:function:string.match(str, pattern, init) string.match()只尋找源字串str中的第一個(gè)配對(duì). 參數(shù)init可選, 指定搜尋過程的起點(diǎn), 默認(rèn)為1
char:function:string.char(97,98,99,100) 將整型數(shù)字轉(zhuǎn)成字符并連接
dump:function
gmatch:function string.gmatch(str, pattern) 返回一個(gè)迭代器函數(shù),每一次調(diào)用這個(gè)函數(shù)李皇,返回一個(gè)在字符串 str 找到的下一個(gè)符合 pattern 描述的子串削茁。如果參數(shù) pattern 描述的字符串沒有找到,迭代函數(shù)返回nil。
reverse:function:string.reverse(arg) 字符串反轉(zhuǎn)
byte:function string.byte("ABCD",4) 轉(zhuǎn)換字符為整數(shù)值(可以指定某個(gè)字符茧跋,默認(rèn)第一個(gè)字符)
format:function:string.format("the value is:%d",4) 返回類似printf的格式化字符串
gsub:function :string.gsub(mainString,findString,replaceString,num) mainString為要替換的字符串慰丛, findString 為被替換的字符,replaceString 要替換的字符瘾杭,num 替換次數(shù)(可以忽略诅病,則全部替換)
lower:function :字符串全部轉(zhuǎn)小寫 string.lower(arg)
pattern匹配格式(略)
- table
通過table來模擬構(gòu)造數(shù)組、模塊(module)粥烁、包(package)和對(duì)象(Object)
setn:function
table.setn(table, nSize)
設(shè)置table中的元素個(gè)數(shù)
insert:function
table.insert (table, [pos,] value):
在table的數(shù)組部分指定位置(pos)插入值為value的一個(gè)元素. pos參數(shù)可選, 默認(rèn)為數(shù)組部分末尾
getn:function
table.getn(table)
返回table中元素的個(gè)數(shù)
foreachi:function
table.foreachi(table, function(i, v))
會(huì)期望一個(gè)從 1(數(shù)字 1)開始的連續(xù)整數(shù)范圍贤笆,遍歷table中的key和value逐對(duì)進(jìn)行function(i, v)操作
maxn:function
table.maxn (table)
指定table中所有正數(shù)key值中最大的key值. 如果不存在key值為正數(shù)的元素, 則返回0。(Lua5.2之后該方法已經(jīng)不存在了,本文使用了自定義函數(shù)實(shí)現(xiàn))
foreach:function:遍歷表的 k,v讨阻;并按照function操作k,v
table.foreach(table, function(i, v))
與foreachi不同的是苏潜,foreach會(huì)對(duì)整個(gè)表進(jìn)行迭代
concat:function
table.concat (table [, sep [, start [, end]]])
concat是concatenate(連鎖, 連接)的縮寫. table.concat()函數(shù)列出參數(shù)中指定table的數(shù)組部分從start位置到end位置的所有元素, 元素間以指定的分隔符(sep)隔開
sort:function
table.sort (table [, comp])
對(duì)給定的table進(jìn)行升序排序 comp是一個(gè)可選的參數(shù), 此參數(shù)是一個(gè)外部函數(shù), 可以用來自定義sort函數(shù)的排序標(biāo)準(zhǔn)
remove:function
table.remove (table [, pos])
返回table數(shù)組部分位于pos位置的元素. 其后的元素會(huì)被前移. pos參數(shù)可選, 默認(rèn)為table長度, 即從最后一個(gè)元素刪起。
注意:
當(dāng)我們獲取 table 的長度的時(shí)候無論是使用 # 還是 table.getn 其都會(huì)在索引中斷的地方停止計(jì)數(shù)变勇,而導(dǎo)致無法正確取得 table 的長度 恤左。可用以下方法代替
function table_leng(t)
local leng=0
for k, v in pairs(t) do
leng=leng+1
end
return leng;
end
11.Lua 模塊與C包
模塊類似于一個(gè)封裝庫搀绣,從 Lua 5.1 開始飞袋,Lua 加入了標(biāo)準(zhǔn)的模塊管理機(jī)制,可以把一些公用的代碼放在一個(gè)文件里链患,以 API 接口的形式在其他地方調(diào)用巧鸭,有利于代碼的重用和降低代碼耦合度。
Lua 的模塊是由變量麻捻、函數(shù)等已知元素組成的 table纲仍,因此創(chuàng)建一個(gè)模塊很簡單,就是創(chuàng)建一個(gè) table贸毕,然后把需要導(dǎo)出的常量郑叠、函數(shù)放入其中,最后返回這個(gè) table 就行明棍。
require 函數(shù)
Lua提供了一個(gè)名為require的函數(shù)用來加載模塊乡革;執(zhí)行 require 后會(huì)返回一個(gè)由模塊常量或函數(shù)組成的 table,并且還會(huì)定義一個(gè)包含該 table 的全局變量
加載機(jī)制
require 用于搜索 Lua 文件的路徑是存放在全局變量 package.path 中摊腋,當(dāng) Lua 啟動(dòng)后沸版,會(huì)以環(huán)境變量 LUA_PATH 的值來初始這個(gè)變量。如果沒有找到該環(huán)境變量兴蒸,則使用一個(gè)編譯時(shí)定義的默認(rèn)路徑來初始化 视粮;
C包
Lua在一個(gè)叫l(wèi)oadlib的函數(shù)內(nèi)提供了所有的動(dòng)態(tài)連接的功能。這個(gè)函數(shù)有兩個(gè)參數(shù):庫的絕對(duì)路徑和初始化函數(shù)橙凳。所以典型的調(diào)用的例子如下:
local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")
loadlib 函數(shù)加載指定的庫并且連接到 Lua蕾殴,然而它并不打開庫(也就是說沒有調(diào)用初始化函數(shù))笑撞,反之他返回初始化函數(shù)作為 Lua 的一個(gè)函數(shù),這樣我們就可以直接在Lua中調(diào)用他区宇。
如果加載動(dòng)態(tài)庫或者查找初始化函數(shù)時(shí)出錯(cuò)娃殖,loadlib 將返回 nil 和錯(cuò)誤信息值戳。我們可以修改前面一段代碼议谷,使其檢測(cè)錯(cuò)誤然后調(diào)用初始化函數(shù):
local path = "/usr/local/lua/lib/libluasocket.so"
-- 或者 path = "C:\windows\luasocket.dll",這是 Window 平臺(tái)下
local f = assert(loadlib(path, "luaopen_socket"))
f() -- 真正打開庫
一般情況下我們期望二進(jìn)制的發(fā)布庫包含一個(gè)與前面代碼段相似的 stub 文件堕虹,安裝二進(jìn)制庫的時(shí)候可以隨便放在某個(gè)目錄卧晓,只需要修改 stub 文件對(duì)應(yīng)二進(jìn)制庫的實(shí)際路徑即可。
將 stub 文件所在的目錄加入到 LUA_PATH赴捞,這樣設(shè)定后就可以使用 require 函數(shù)加載 C 庫了逼裆。
12.Lua 元表(Metatable)
Lua 提供了元表(Metatable),允許我們改變table的行為赦政,每個(gè)行為關(guān)聯(lián)了對(duì)應(yīng)的元方法
為表提供一些有用的行為胜宇,例如:表之間的算術(shù)運(yùn)算。恢着。桐愉。
有兩個(gè)很重要的函數(shù)來處理元表:
setmetatable(table,metatable): 對(duì)指定 table 設(shè)置元表(metatable),如果元表(metatable)中存在 __metatable 鍵值掰派,setmetatable 會(huì)失敗从诲。
getmetatable(table): 返回對(duì)象的元表(metatable)。
在表的元表中設(shè)置對(duì)應(yīng)的索引和函數(shù)
13 協(xié)程(協(xié)同程序有點(diǎn)類似同步的多線程 )
coroutine.create()
創(chuàng)建coroutine靡羡,返回coroutine系洛, 參數(shù)是一個(gè)函數(shù),當(dāng)和resume配合使用的時(shí)候就喚醒函數(shù)調(diào)用
coroutine.resume()
重啟coroutine略步,和create配合使用
coroutine.yield()
掛起coroutine描扯,將coroutine設(shè)置為掛起狀態(tài),這個(gè)和resume配合使用能有很多有用的效果
coroutine.status()
查看coroutine的狀態(tài)
注:coroutine的狀態(tài)有三種:dead趟薄,suspend荆烈,running,具體什么時(shí)候有這樣的狀態(tài)請(qǐng)參考下面的程序
coroutine.wrap()
創(chuàng)建coroutine竟趾,返回一個(gè)函數(shù)憔购,一旦你調(diào)用這個(gè)函數(shù),就進(jìn)入coroutine岔帽,和create功能重復(fù)
coroutine.running()
返回正在跑的coroutine玫鸟,一個(gè)coroutine就是一個(gè)線程,當(dāng)使用running的時(shí)候犀勒,就是返回一個(gè)corouting的線程號(hào)
Eg
-- coroutine_test.lua 文件
co = coroutine.create(
function(i)
print(i);
end
)
coroutine.resume(co, 1) -- 1
print(coroutine.status(co)) -- dead
print("----------")
co = coroutine.wrap(
function(i)
print(i);
end
)
co(1)
print("----------")
co2 = coroutine.create(
function()
for i=1,10 do
print(i)
if i == 3 then
print(coroutine.status(co2)) --running
print(coroutine.running()) --thread:XXXXXX
end
coroutine.yield()
end
end
)
coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3
print(coroutine.status(co2)) -- suspended
print(coroutine.running())
print("----------")
返回結(jié)果:
1
dead
1
1
2
3
running
thread: 0x7fb801c05868 false
suspended
thread: 0x7fb801c04c88 true
理解:
coroutine在底層實(shí)現(xiàn)就是一個(gè)線程屎飘。
當(dāng)create一個(gè)coroutine的時(shí)候就是在新線程中注冊(cè)了一個(gè)事件妥曲。
當(dāng)使用resume觸發(fā)事件的時(shí)候,create的coroutine函數(shù)就被執(zhí)行了钦购,當(dāng)遇到y(tǒng)ield的時(shí)候就代表掛起當(dāng)前線程檐盟,等候再次resume觸發(fā)事件
14 全局變量
所有的全局變量(_G)都是放在一個(gè)特定Lua table 的諸個(gè)域中,這個(gè)特定的table 叫
作environment(環(huán)境) table 或者簡稱為 環(huán)境 押桃。每個(gè)函數(shù)都有對(duì)一個(gè)環(huán)境的
引用葵萎, 所以一個(gè)函數(shù)中可見的所有全局變量都放在這個(gè)函數(shù)所引用的環(huán)境表(environment table)中。當(dāng)一個(gè)函數(shù)被創(chuàng)建出來唱凯,它會(huì)從創(chuàng)建它的函數(shù)中繼承其環(huán)境羡忘,你可以調(diào)用getfenv 取得其環(huán)境。 如果想改變環(huán)境磕昼,可以調(diào)用 setfenv
關(guān)聯(lián)在線程上的環(huán)境被稱作全局環(huán)境(_G)
_ENV 是當(dāng)前運(yùn)行的函數(shù)的環(huán)境
每個(gè)被編譯的 Lua 代碼塊都會(huì)有一個(gè)外部的局部變量叫 _ENV
多數(shù)情況下_ENV=_G
chunk語句塊
Lua 把一個(gè)chunk 當(dāng)作一個(gè)擁有不定參數(shù)的匿名函數(shù)處理卷雕。正是這樣,
chunk 內(nèi)可以定義局部變量票从,接收參數(shù)漫雕,并且返回值。
局部變量被更內(nèi)層的函數(shù)中使用的時(shí)候峰鄙, 它被內(nèi)層函數(shù)稱作 upvalue
Lua 是一個(gè)嵌入式的擴(kuò)展語言浸间, 所有的 Lua 動(dòng)作都是從宿主程序的C 代碼調(diào)用
Lua 庫中的一個(gè)函數(shù)開始的。在 Lua 編譯或運(yùn)行的任何時(shí)候發(fā)生了錯(cuò)誤先馆,控制權(quán)都會(huì)交還給C 发框, 而 C 可以來做一些恰當(dāng)?shù)拇胧?/p>
Lua與C的相互調(diào)用 Lua 的C API
也就是宿主程序跟 Lua 通訊用的一組C 函數(shù)
Lua 使用一個(gè)虛擬棧來和C 傳遞值。棧上的的每個(gè)元素都是一個(gè)Lua 值
幾乎所有的API調(diào)用都是對(duì)棧上的值進(jìn)行操作
在c中調(diào)用lua:將lua數(shù)據(jù)和函數(shù)交給C使用
在調(diào)用C API時(shí)有幾個(gè)重要的頭文件:
(1)lua.h:Lua基礎(chǔ)函數(shù)庫煤墙,lua_前綴
(2)lauxlib.h:輔助庫梅惯,luaL_前綴,利用lua.h實(shí)現(xiàn)的更高層的抽象
(3)lualib.h:為了保持Lua的苗條仿野,所有的標(biāo)準(zhǔn)庫以單獨(dú)的包提供铣减,所以如果你不需要就不會(huì)強(qiáng)求你使用它們。頭文件lualib.h定義了打開這些庫的函數(shù)脚作。例如葫哗,調(diào)用luaopen_io,以創(chuàng)建io table并注冊(cè)I/O函數(shù)(io.read,io.write等等)到Lua環(huán)境中球涛。
lua_State* L = lua_open();//定義lua狀態(tài)機(jī)
luaL_openlibs(L); //初始化
讀寫Lua全局變量的函數(shù)(lua_pushXX劣针、lua_toXX)、調(diào)用Lua函數(shù)的函數(shù)(lua_pcall)亿扁、運(yùn)行Lua代碼片斷的函數(shù)捺典、注冊(cè)C函數(shù)(lua_register)然后可以在Lua中被調(diào)用的函數(shù),等等从祝。
棧底為1襟己,依次向上加引谜;也可以棧頂為-1,依次向下減
lua_push*:壓入棧元素 參數(shù)為狀態(tài)機(jī)和壓人棧中的數(shù)據(jù)
void lua_pushnil (lua_State *L);
void lua_pushboolean (lua_State *L, int bool);
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s, size_t length);
void lua_pushstring (lua_State *L, const char s);
lua_to:從棧中獲得值擎浴。即使給定的元素類型不正確员咽,調(diào)用這些函數(shù)也沒問題。
int lua_toboolean (lua_State *L, int index);
double lua_tonumber (lua_State *L, int index);
const char * lua_tostring (lua_State *L, int index);
size_t lua_strlen (lua_State *L, int index):返回字符串的實(shí)際長度贮预。
int lua_checkstack(lua_State *L, int sz):檢查棻词遥空間。默認(rèn)有20個(gè)空閑的記錄萌狂,lua.h中的LUA_MINSTACK宏定義了這個(gè)常量档玻。
int lua_is... (lua_State *L, int index):檢查一個(gè)元素能否被轉(zhuǎn)換成指定的類型怀泊。
int lua_type (lua_State L, int idx):返回棧中元素的類型茫藏;
const char lua_typename(lua_State *L, int tp):返回type對(duì)應(yīng)的名字字符串,第二個(gè)參數(shù)為lua_type返回的類型
void luaL_checktype (lua_State *L, int arg, int t):返回參數(shù)arg是否是類型t霹琼,第三個(gè)參數(shù)為lua_type的取值务傲。
在lua.h頭文件中,每種類型都被定義為一個(gè)常量:LUA_TNIL枣申、LUA_TBOOLEAN售葡、LUA_TNUMBER、LUA_TSTRING忠藤、LUA_TTABLE挟伙、LUA_TFUNCTION、LUA_TUSERDATA以及LUA_TTHREAD模孩。
int lua_gettop (lua_State *L):返回棧中元素個(gè)數(shù)尖阔,它也是棧頂元素的索引。
void lua_settop (lua_State *L, int index):設(shè)置棧頂元素的索引榨咐,相當(dāng)于設(shè)置棧的大小介却。如果開始的棧頂高于新的棧頂,頂部的值被丟棄块茁。否則齿坷,為了得到指定的大小這個(gè)函數(shù)壓入相應(yīng)個(gè)數(shù)的空值(nil)到棧上。lua_settop(L,0):清空堆棧数焊。
#define lua_pop(L,n) lua_settop(L, -(n)-1):宏定義永淌,彈出n個(gè)元素。
void lua_pushvalue (lua_State *L, int index):壓入堆棧上指定索引的一個(gè)摶貝到棧頂佩耳,等于拷貝index處的元素遂蛀,然后添加到棧頂。
void lua_remove (lua_State *L, int index):移除指定索引的元素蚕愤,并將其上面所有的元素下移來填補(bǔ)這個(gè)位置的空白答恶。
void lua_insert (lua_State *L, int index):移動(dòng)棧頂元素到指定索引的位置饺蚊,并將這個(gè)索引位置上面的元素全部上移至棧頂被移動(dòng)留下的空隔。
void lua_replace (lua_State *L, int index):從棧頂彈出元素值并將其設(shè)置到指定索引位置悬嗓,沒有任何移動(dòng)操作
C調(diào)用Lua(lua提供入棧污呼,C使用出棧)
使用API調(diào)用函數(shù)的方法是很簡單的:
首先,將被調(diào)用的函數(shù)入棧包竹;
第二燕酷,依次將所有參數(shù)入棧;
第三周瞎,使用lua_pcall調(diào)用函數(shù)苗缩;
最后,從棧中獲取函數(shù)執(zhí)行返回的結(jié)果声诸。
在將結(jié)果入棧之前酱讶,lua_pcall會(huì)將棧內(nèi)的函數(shù)和參數(shù)移除。
如果lua_pcall運(yùn)行時(shí)出現(xiàn)錯(cuò)誤彼乌,lua_pcall會(huì)返回一個(gè)非0的結(jié)果泻肯,并將錯(cuò)誤信息入棧(依然會(huì)先移除棧內(nèi)函數(shù)和參數(shù))
int lua_pcall (lua_State L, int nargs, int nresults, int msgh);
//調(diào)用棧頂函數(shù),指定參數(shù)個(gè)數(shù):nargs慰照,返回結(jié)果個(gè)數(shù):nresults灶挟,和錯(cuò)誤函數(shù)msgh 錯(cuò)誤處理函數(shù) 在棧上的索引位置
Lua 調(diào)用C(c提供入棧,Lua使用出棧)
lua_State L = luaL_newstate();
luaL_openlibs(L);
//注冊(cè)被Lua調(diào)用的C函數(shù)毒租,參數(shù)"add"表示Lua調(diào)用時(shí)使用的全局函數(shù)名稚铣,func為被調(diào)用的C函數(shù)
lua_register(L, "add", func);
luaL_dostring("print(add(1, 2))");//通過add名使用C函數(shù)
lua_close(L);
void lua_register (lua_State *L, const char *name, lua_CFunction f);//把 C 函數(shù) f 設(shè)到全局變量 name 中