Program in Lua

第一篇 語言

第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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容