第一篇 語言
第0章 序言
Lua僅讓你用少量的代碼解決關鍵問題。
Lua所提供的機制是C不擅長的:高級語言,動態結構,簡潔,易于測試和調試等。
Lua的獨有特征:
(1)可擴展性
(2)簡單
(3)高效率
(4)與平臺無關
第1章 起點
1.1 Chunks
Lua執行的每一塊語句,比如一個文件或者交互模式下的每一行都是一個Chunk
交互模式下 ?可調用os.exit()退出
另一個連接外部chunk的方式是使用dofile函數,dofile函數加載文件并執行它
loadfile(): 只編譯,不運行。
dofile(): 執行。
require(): 只執行一次,會保存已經加載的文件。
當且僅當一個變量不等于nil時,這個變量存在。
在運行參數之前,Lua會查找環境變量LUA_INIT的值,若存在且值為@filename,Lua將加載指定文件,若不以@開頭,假定為Lua代碼并執行它。
第2章 類型和值
Lua認為0和空串都是真
盡管字符串和數字可以自動轉換,但兩者不同,10 == “10”是錯的。
tonumber()將string轉成數字
tostring()將number轉成字符串
函數是第一類值(和其他變量相同)
標準庫包括:string庫,math庫,debug庫,os庫,table庫,io庫
第3章 表達式
第6章 再論函數
6.3 正確的尾調用 Proper tail calls
Lua可以正確地處理尾調用。
尾調用:當函數最后一個動作是調用另外一個函數時,稱這種調用為尾調用。
可以在尾部以較低代價進行遞歸。
第7章 迭代器與泛型for
7.1 迭代器與閉包
Lua中使用函數來描述迭代器,每次調用該函數就返回集合的下一個元素。閉包機制可以很容易實現該任務。
8. 編譯,運行,調試
dofile
loadfile:編譯代碼為中間碼并且返回編譯后的chunk作為一個函數,而不執行代碼
loadfile返回nil和錯誤信息,使用起來較dofile更自由
loadstring與loadfile相似
都不拋出錯誤,只會返回nil加上錯誤信息
Lua中的函數定義發生在運行時的賦值而不是發生在編譯時。
loadstring(s)()//快速運行
loadstring不關心詞法范圍,總是在全局環境中編譯他的串
8.1 require函數
require與dofile完成同樣的功能但有兩點不同
(1)require會搜索目錄加載文件
(2)require會判斷是否文件已經加載避免重復加載同一文件。
require的路徑是一個模式列表
require關注的問題只有分號(模式之間的分隔符)和問號
Lua首先檢查全局變量LUA_PATH是否為一個字符串,若是,則此串為路徑,否則require使用固定的路徑
8.2 C Packages
Lua在一個叫loadlib的函數內提供了所有的動態鏈接的功能
用法:
local path = "/usr/local/lua/lib/libXXX.so"
local f = assert(loadlib(path, "luaopen_socket"))
loadlib函數加載指定的庫并連接到Lua,然而并不打開庫(也就是說沒有調用初始化函數)
8.3 錯誤
Lua中調用error("invalid input")拋出錯誤
n = assert (io.read("*number"), "invalid input")也可拋出錯誤
當函數遇到異常有兩個基本的動作,返回錯誤代碼或者拋出錯誤
file = assert(io.open("no-file", "r"))
io.open返回的第二個結果(錯誤信息)作為assert的第二個參數
8.4 異常和錯誤處理
如果在Lua中需要處理錯誤,需要使用pcall函數封裝你的代碼
if pcall(foo) then
-- no errors while running 'foo'
else
-- foo raised an error
end
8.5 錯誤信息和回跟蹤(tracebacks)
當pcall返回錯誤信息的時候他已經釋放了保存錯誤發生情況的棧的信息。如果想得到traceback,必須在pcall返回以前獲取。xpcall接受兩個參數,調用函數和錯誤處理函數,當錯誤發生時,Lua會在棧釋放以前調用錯誤處理函數,因此可以使用debug庫收集錯誤相關的信息。
常用的debug處理函數debug與debug.traceback,前者給出Lua的提示符,可以動手查看錯誤發生時的情況,后者通過traceback創建更多的錯誤信息,后者是控制臺解釋器用來構建錯誤信息的函數
print(debug.traceback) ?-- ?隨時查看當前運行的traceback信息
第9章 協同程序
9.1
9.2 管道與過濾器
協同是一種非搶占式的多線程。
協同模式下,任務間的切換代價較小,與函數調用相當,因此讀寫可以很好的協同處理
9.3 用作迭代器的協同
9.4 非搶占式多線程
不需要同步機制
第10章 完整示例
10.1 Lua作為數據描述語言使用
entry{a='a', b ='b'}
第12章 數據文件與持久化
實現一個健壯的讀取數據文件的程序是很困難的
數據描述是Lua的主要應用之一
12.1 序列化
第13章 Metatables and Metamethods
(在lua代碼中的普通表,不能作為userdata的metatable。必須使用luaL_newmetatable創建的表才能作為userdata的metatable。)
Lua默認創建一個不帶metatable的新表
t= {}
print(getmetatable(t)) -- print nil
t1 = {}
setmetatable(t, t1)
assert(getmetatable(t) == t1)
任何一個表都可以是其他一個表的metatable,一組相關的表可以共享一個metatable(描述他們共同的行為)。一個表也可以是自身的metatable(描述其私有行為)
13.1 算術運算的Metamethods
13.4 表相關的Metamethods
13.4.1 the __index Metamethod
當訪問一個表中不存在的域,會觸發lua解釋器去查找__index Metamethod,如果不存在,返回nil,如果存在則由__index metamethod 返回結果
__index也可以是一個表,若是函數,Lua將table和缺少的域作為參數調用這個函數
一個函數的代價雖然稍微高點,但提供了更多的靈活性:可以實現多繼承,隱藏,和其他一些變異的機制。
13.4.2 the __newindex Metamethod
用于對表更新,__index則用來對表訪問。當給表的一個缺少的域賦值,解釋器就會查找__newindex metamethod:如果存在則調用這個函數而不進行賦值操作。如果metamethod是一個表,解釋器對指定的那個表,而不是原始的表進行賦值操作。
還有一個raw函數可以繞過Metamethod: 如調用rawset(t,k,v)不調用任何Metamethod對表t的k域賦值為v。
13.4.3 有默認值的表
在一個普通的表中任何域的默認值都是nil,很容易通過metatables來改變默認值
訪問情況的唯一方法就是保持表為空。如果我們想監控一個表的所有訪問情況,我們應該為真實的表創建一個代理。
該設計不允許遍歷表
如果想監控多張表,
13.4.4 監控表
捕獲對一個表的所有
13.4.5 只讀表
采用代理的思想很容易實現一個只讀表,需要做的只是當我們監控到企圖修改表時拋出錯誤。
第14章 環境
Lua將環境本身存儲在一個全局變量_G中,_G._G等于_G
14.1 使用動態名字訪問全局變量
元編程meta-programming
value = _G[varname]
14.2 聲明全局變量
14.3 非全局的環境
可以使用setfenv函數來改變一個函數的環境,該函數接受函數和新的環境作為參數。除了使用函數本身,還可以指定一個數字表示棧頂的活動函數,數字1表示當前函數,數字2代表調用當前函數的函數
第15章 Packages
Lua并沒有提供明確的機制來實現packages,主要思想是:像標準庫一樣,使用表來描述package。
15.1 基本方法
都加在package表中
這種使用表來實現的包和真正的包的功能并不完全相同。
首先,我們對每一個函數定義都必須顯式地在前面加上包的名稱。第二,同一包內的函數相互調用必須在被調用的函數前指定包名。
改進方法:
可以使用固定的局部變量名,然后將這個局部變量賦值給最終的包。
這樣至少可以不再依賴于固定的包名。
15.2 私有成員
缺點:當修改函數的狀態,必須修改函數的調用方式
解決方案:可以將package內的所有函數都聲明為局部的,最后將他們放在最終的表中。
15.3 包與文件
15.4 使用全局表
15.5 其他一些技巧
第16章 面向對象程序設計
16.1 類
如果有a和b兩個對象,想讓b作為a的prototype只需要
setmetatable(a, {__index = b})
function Account:new (o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
getmetatable(a).__index.deposit(a, 100)
Account.deposit(a, 100)
16.2 繼承
16.3 多重繼承
實現關鍵:將函數用作__index,當一個表的metatable存在一個__index函數時。如果Lua調用一個原始表中不存在的函數,Lua將調用這個__index指定的函數,這樣可以用__index實現在多個父類中查找子類不存在的域。
16.4 私有性
Lua沒有打算被用來進行大型的程序設計,其目標定于小型到中型的程序設計,通常作為大型系統的一部分。
Lua的另一個目標是靈活性,提供程序員元機制(meta-mechanisms),通過他可以實現很多不同的機制。
設計思想為:每個對象用兩個表來表示:一個描述狀態,另一個描述操作(或者叫接口),對象本身通過第二個表來訪問,也就是說,通過接口來訪問對象。為了避免未授權的訪問,表示狀態的表中不涉及到操作;表示操作的表中也不涉及到狀態,取而代之的是狀態被保存在方法的閉包內,
16.5 Single-method
第20章 String庫
20.1 模式匹配函數
20.2 模式
字符類指可以匹配一個特定字符集合內任何字符的模式項。
例:字符類%d匹配任意數字
模式串
字符類的補集
第四篇 C API
第24章 C API縱覽
C API 是一個C代碼和Lua進行交互的函數集。由以下幾部分組成:
讀寫Lua全局變量的函數
調用Lua函數的函數
運行Lua代碼片段的函數
注冊C函數然后可以在Lua中被調用的函數
等等
C API遵循C語言的語法形式。API中的大部分函數并不檢查他們參數的正確性。
API重點放在了靈活性和簡潔性方面,有時候以犧牲方便實用為代價的。
在C和Lua通信關鍵內容在于一個虛擬的棧。
棧的使用解決了C和Lua之間兩個不協調的問題。
24.1 第一個示例程序
lua.h中定義了Lua提供的基礎函數,其中包括
創建一個新的Lua環境的函數如lua_open
調用Lua函數的函數如lua_pcall
讀取/寫入Lua環境的全局變量的函數。
注冊可以被Lua代碼調用的新函數的函數
等等
所有在lua.h中被定義的都有一個lua_前綴
luaxlib.h定義了輔助庫提供的函數,其中所有函數都以luaL_打頭。輔助庫利用lua.h中提供的基礎函數提供了更高層次上的抽象;所有Lua標準庫都使用了auxlib。
24.2 堆棧
Lua和C之間交換數據時面臨兩個問題:動態與靜態類型系統的不匹配和自動與手動內存管理的不一致。
Lua API沒有定義任何類似lua_Value的類型,替代的方案是:用一個抽象的棧在lua與C之間交換值。棧中的每一條記錄都可以保存任何lua值
Lua以嚴格的LIFO規則來操作棧。當調用Lua時,只會改變棧頂部分。C代碼有更多自由,可以查詢棧上的任何元素,甚至在任何一個位置插入和刪除元素。
24.2.1 壓入元素
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)//lua字符串
void lua_pushstring (lua_State* L, const char* s)//以/0結尾
Lua從來不保持一個指向外部字符串的指針,對于它保持的所有字符串,Lua要么做一份內部的拷貝要么重新利用已經存在的字符串。故函數返回后,可以自由修改或是釋放C中緩沖區
無論何時壓入元素到棧上,有責任確保在棧上有空間來做這件事情。
Lua調用C的時候,則至少有20個空閑的記錄。lua.h中的LUA_MINSTACK宏定義了該常量。
int lua_checkstack ( lua_State* L, int sz);
檢測棧上是否有足夠需要的空間。
24.2.2 查詢元素
棧中第一個元素索引為1,-1為棧頂元素
int lua_is... (lua_State* L, int index)
lua_isnumber和lua_isstring函數不檢查這個值是否是指定的類型,而是看它是否能被轉換成指定的那種類型。
為了從棧中獲得值,使用lua_to*函數
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);
lua_tostring函數返回一個指向字符串的內部拷貝的指針。不能修改,只要該指針對應的值在棧內,Lua會保證這個指針一直有效;
原則:永遠不要將指向Lua字符串的指針保存到訪問他們的外部函數中。
24.2.3 其他堆棧操作
int lua_gettop (lua_State* L);//返回堆棧中元素個數。
void lua_settop(L, int index);//lua_settop(L, 0)清空堆棧
基于上面函數,提供
#define lua_pop(L, n) lua_settop(L, -(n) - 1)
void lua_pushvalue (L, int index); // 壓入堆棧上指定索引的一個拷貝到棧頂
void lua_remove(L, int index);//
void lua_insert(L, int index);
void lua_replace (L, int index);
24.3 C API的錯誤處理
Lua中所有結構都是動態的,按需增長,當可能時又會縮減。因此,內存分配失敗的可能性在Lua中是普遍的。
24.3.1 應用程序中的錯誤處理
運行在非保護模式下,Lua遇錯誤后,調用panic函數并退出應用,可使用lua_atpanic函數設置自己的panic函數。
若不想應用退出,必須在保護模式下運行代碼。
所有Lua代碼使用lua_pcall()來運行,即使錯誤,也返回一個錯誤代碼。
如果也想保護所有與Lua交互的C代碼,可以使用lua_cpcall
24.3.2 類庫中的錯誤處理
C庫函數發現錯誤只要簡單調用lua_error,該函數會清理所有在Lua中需要被清理的,然后和錯誤信息一起回到最初的執行lua_pcall的地方。
第25章 擴展你的程序
作為配置語言是Lua的一個重要應用。
25.1 表操作
將key與value壓入棧中,同時table位于idx處,調用lua_settable(L, idx)
25.2 調用Lua函數
(1)將被調用的函數入棧
(2)依次將所有參數入棧
(3)使用lua_pcall調用函數
(4)從棧中獲取函數執行返回的結果
lua_pcall()
25.3 通用的函數調用
使用C的vararg來封裝對Lua函數的調用,
第26章 調用C函數
擴展Lua的基本方法之一:為應用程序注冊新的C函數到Lua中去
Lua調用C函數時,用來交互的棧不是全局變量,每一個函數都有他自己的私有棧。當Lua調用C函數時,第一個參數總是在這個私有棧的index=1的位置。
26.1 C函數
在Lua中注冊的函數必須符合原型
typedef int (*lua_CFunction) (lua_State* L);
返回一個表示返回值個數的數字
函數在將返回值入棧之前不需要清理棧,函數返回之后,Lua自動的清除棧中返回結果下面的所有內容。
lua_pushcfunction():獲取指向C函數的指針,并在Lua中創建一個function類型的值來表示這個函數。
lua_pushcfunction(l, l_sin);
lua_setglobal(l, "mysin");
26.2 C 函數庫
通常C庫都有一個外部的用來打開庫的函數。
luaL_openlib函數接受一個C函數的列表和他們對應的函數名,并且作為一個庫在一個table中注冊所有這些函數。
(1)定義庫函數
(2)聲明一個luaL_reg數組,保存所有的函數和他們對應的名字,且以{NULL,NULL}結尾
(3)使用luaL_openlib聲明主函數
luaL_openlib(L, "mylib", mylib, 0)
還可以為庫中所有函數注冊公共的upvalues。不需要時,最后一個參數為0.
luaL_openlib返回的時候,將保存庫的表放到棧內。
完成庫的代碼編寫之后,必須將它鏈接到Lua解釋器。最常用的方式是使用動態鏈接庫。
可在lua中直接使用loadlib加載你剛才定義的函數庫。
mylib = loadlib("fullname-of-your-library", "luaopen_mylib")
然后定義mylib(),將運行luaopen_mylib()打開定義的函數庫。
當打開一個新的狀態時,必須打開這個新定義的函數庫。
當解釋器創建新的狀態的時候會調用這個宏
#define LUA_EXTRALIBS {"mylib", XXXXX}
第27章 撰寫C函數的技巧
27.1 數組操作
lua_settable ? ? ? ? ? ? ?lua_gettable
出于性能考慮的數組操作
void lua_rawgeti (lua_State * L, int index, int key)
void lua_rawseti (lua_State * L, int index, int key)
27.2 字符串處理
當C函數接受一個來自lua的字符串作為參數時,有兩個規則必須遵守:當字符串正在被訪問的時候不要將其出棧,永遠不要修改字符串。
27.3 在C函數中保存狀態
用于C語言保留一些非局部的數據
Lua提供了一個獨立地被稱為registry的表,C代碼可以自由使用,但Lua代碼不能訪問。
27.3.1 the registry
registry位于一個由LUA_REGISTRYINDEX定義的值所對應的假索引的位置。一個假索引除了他對應的值不在棧中之外,其他都類似于棧中的索引。
第28章 User-defined Types in C
使用C函數來擴展Lua功能。
如何使用C中新類型來擴展Lua。
28.1 Userdata
void * lua_newuserdata (lua_State* L, size_t size);
按照指定大小分配一塊內存,將對應的userdatum放到棧內,并返回內存塊的地址。
luaL_checkint()
luaL_argcheck()
28.2 Metatables