lua學習知識點筆記
官網 http://www.lua.org
Lua語言從一開始就被設計為能與C/C++及其他常用語言開發的軟件集成在一起使用的語言,這種設計帶來了非常多的好處。一方面,Lua語言不需要在性能、與三方軟件交互等C語言已經非常完善的方面重復“造輪子”,可以直接依賴C語言實現上述特性,因而Lua語言非常精簡;另一方面,通過引入安全的運行時環境、自動內存管理、良好的字符串處理能力和可變長的多種數據類型,Lua語言彌補了C語言在非面向硬件的高級抽象能力、動態數據結構、魯棒性、調試能力等方面的不足。
Lua語言強大的原因之一就在于它的標準庫,這不是偶然,畢竟擴展性本身就是Lua語言的主要能力之一。Lua語言中的許多特性為擴展性的實現提供了支持:動態類型使得一定程度的多態成為了可能,自動內存管理簡化了接口的實現(無須關心內存的分配/釋放及處理溢出),作為第一類值的函數支持高度的泛化,從而使得函數更加通用。
Lua語言除了是一門可擴展的語言外,還是一門膠水語言(glue language)。Lua語言支持組件化的軟件開發方式,通過整合已有的高級組件構建新的應用。這些組件通常是通過C/C++等編譯型強類型語言編寫的,Lua語言充當了整合和連接這些組件的角色
保留關鍵字
以下列出了 Lua 的保留關鍵字。保留關鍵字不能作為常量或變量或其他用戶自定義標示符:
|
and |
break |
do |
else |
|
elseif |
end |
false |
for |
|
function |
if |
in |
local |
|
nil |
not |
or |
repeat |
|
return |
then |
true |
until |
|
while |
goto |
|
|
一般約定,以下劃線開頭連接一串大寫字母的名字(比如 _VERSION)被保留用于 Lua 內部全局變量。大小寫敏感
注釋
兩個連續的連字符(--)表示單行注釋的開始(從--之后直到此行結束都是注釋),使用兩個連續的連字符加兩對連續左方括號表示長注釋或多行注釋的開始(直到兩個連續的右括號為止,中間都是注釋)
---[[
長注釋
--]]
變量
全局變量(Global Variable)無須聲明即可使用,使用未經初始化的全局變量也不會導致錯誤。當使用未經初始化的全局變量時,得到的結果是nil
Lua語言中的變量在默認情況下是全局變量,所有的局部變量在使用前必須聲明。與全局變量不同,局部變量的生效范圍僅限于聲明它的代碼塊。
盡可能地使用局部變量是一種良好的編程風格。
類型
有8種基本類型:nil(空)、boolean(布爾)、number(數值)、string(字符串)、userdata(用戶數據)、function(函數)、thread(線程)和table(表)
在Lua 5.2及之前的版本中,所有的數值都以雙精度浮點格式表示。從Lua 5.3版本開始,Lua語言為數值格式提供了兩種選擇:被稱為integer的64位整型和被稱為float的雙精度浮點類型(注意,在本書中“float”不代表單精度類型)。對于資源受限的平臺,我們可以將Lua 5.3編譯為精簡Lua(Small Lua)模式,在該模式中使用32位整型和單精度浮點類型。
字符串
Lua語言中的字符串是不可變值(immutable value),字符串是一串字節組成的序列.
可以使用長度操作符(length operator)(#)獲取字符串的長度。
可以使用一對雙引號或單引號來聲明字符串常量(literal string)
像長注釋/多行注釋一樣,可以使用一對雙方括號來聲明長字符串/多行字符串常量。被方括號括起來的內容可以包括很多行,并且內容中的轉義序列不會被轉義。此外,如果多行字符串中的第一個字符是換行符,那么這個換行符會被忽略。可以在兩個左方括號之間加上任意數量的等號,如[===[。這樣,字符串常量只有在遇到了包含相同數量等號的兩個右方括號時才會結束(就前例而言,即]===])
字符串連接 ..
字符串標準庫默認處理的是8 bit(1 byte)字符。這對于某些編碼方式(例如ASCII或ISO-8859-1)適用,但對所有的Unicode編碼來說都不適用。
函數string.format是用于進行字符串格式化和將數值輸出為字符串的強大工具,該函數會返回第一個參數(也就是所謂的格式化字符串(format string))的副本,其中的每一個指示符(directive)都會被替換為使用對應格式進行格式化后的對應參數。
utf8標準庫。函數utf8.len返回指定字符串中UTF-8字符(代碼點)的個數
表
表(Table)是Lua語言中最主要(事實上也是唯一的)和強大的數據結構。使用表,Lua語言可以以一種簡單、統一且高效的方式表示數組、集合、記錄和其他很多數據結構。Lua語言也使用表來表示包(package)和其他對象。當調用函數math.sin時,我們可能認為是“調用了math庫中函數sin”;而對于Lua語言來說,其實際含義是“以字符串"sin"為鍵檢索表math”。
a = {} 創建一個表
a.name等價于a["name"]
當被用作表索引時,任何能夠被轉換為整型的浮點數都會被轉換成整型數。例如,當執行表達式a[2.0]=10時,鍵2.0會被轉換為2。相反,不能被轉換為整型數的浮點數則不會發生上述的類型轉換
表構造器(Table Constructor)是用來創建和初始化表的表達式,也是Lua語言中獨有的也是最有用、最靈活的機制之一。正如我們此前已經提到的,最簡單的構造器是空構造器{}。表構造器也可以被用來初始化列表,例如,下例中使用字符串"Sunday"初始化了days[1](構造器第一個元素的索引是1而不是0)
days={“MON”, “ TUE“}
數組(array)或列表(list)
如果想表示常見的數組(array)或列表(list),那么只需要使用整型作為索引的表即可。同時,也不需要預先聲明表的大小,只需要直接初始化我們需要的元素即可:
在Lua語言中,數組索引按照慣例是從1開始的
獲取序列長度的操作符#
in pairs遍歷過程中元素的出現順序可能是隨機的
in ipairs迭代器 遍歷是按照順序進行的
函數
Lua語言中一種與眾不同但又非常有用的特性是允許一個函數返回多個結果(Multiple Results)
參數列表中的三個點(...)表示該函數的參數是可變長的。
函數table.unpack與函數table.pack的功能相反。pack把參數列表轉換成Lua語言中一個真實的列表(一個表),而unpack則把Lua語言中的真實的列表(一個表)轉換成一組返回值,進而可以作為另一個函數的參數被使用
Lua語言也為面向對象風格的調用(object-oriented call)提供了一種特殊的語法,即冒號操作符。形如o:foo(x)的表達式意為調用對象o的foo方法。
關系運算
< > >= <=
==用于相等性測試,~=用于不等性測試
輸入輸出
由于Lua語言強調可移植性和嵌入性,所以Lua語言本身并沒有提供太多與外部交互的機制。在真實的Lua程序中,從圖形、數據庫到網絡的訪問等大多數I/O操作,要么由宿主程序實現,要么通過不包括在發行版中的外部庫實現。單就Lua語言而言,只提供了ISO C語言標準支持的功能,即基本的文件操作等
調用io.read("l")會返回當前輸入流的下一行,不包括換行符在內;調用io.read("L")與之類似,但會保留換行符(如果文件中存在)。當到達文件末尾時,由于已經沒有內容可以返回,該函數會返回nil。選項"l"是函數read的默認參數。
函數io.open來打開一個文件,該函數仿造了C語言中的函數fopen。這個函數有兩個參數,一個參數是待打開文件的文件名,另一個參數是一個模式(mode)字符串。
函數os.execute用于運行系統命令,它等價于C語言中的函數system。
io.popen。[4]同函數os.execute一樣,該函數運行一條系統命令,但該函數還可以重定向命令的輸入/輸出,從而使得程序可以向命令中寫入或從命令的輸出中讀取。
控制結構
Lua語言提供了一組精簡且常用的控制結構(control structure),包括用于條件執行的if以及用于循環的while、repeat和for。所有的控制結構語法上都有一個顯式的終結符:end用于終結if、for及while結構,until用于終結repeat結構。
控制結構的條件表達式(condition expression)的結果可以是任何值。請記住,Lua語言將所有不是false和nil的值當作真(特別地,Lua語言將0和空字符串也當作真)。
時間日期
函數os.time
函數os.date
函數os.clock通常具有比秒更高的精度,因此其返回值為一個浮點數。具體的精度與平臺相關,在POSIX系統中通常是1毫秒。
編譯
Lua語言會在運行源代碼之前先對其進行預編譯。Lua語言也允許我們以預編譯的形式分發代碼。生成預編譯文件(也被稱為二進制文件,binary chunk)的最簡單的方式是,使用標準發行版中附帶的luac程序。
預編譯形式的代碼不一定比源代碼更小,但是卻加載得更快。預編譯形式的代碼的另一個好處是,可以避免由于意外而修改源碼。然而,與源代碼不同,蓄意損壞或構造的二進制代碼可能會讓Lua解析器崩潰或甚至執行用戶提供的機器碼。
模塊和包
Lua語言從5.1版本開始為模塊和包(package,模塊的集合)定義了一系列的規則。
當函數的參數只有一個字符串常量時括號是可以省略的,而且一般在使用require時按照慣例也會省略括號
local m = require(“math”)
local m = require “math”
面向對象(Object-Oriented)編程
使用參數self是所有面向對象語言的核心點。大多數面向對象語言都向程序員隱藏了這個機制,從而使得程序員不必顯式地聲明這個參數(雖然程序員仍然可以在方法內使用self或者this)。Lua語言同樣可以使用冒號操作符(colon operator)隱藏該參數。使用冒號操作符,我們可以將上例重寫為a2:withdraw(260.00)
冒號的作用是在一個方法調用中增加一個額外的實參,或在方法的定義中增加一個額外的隱藏形參。冒號只是一種語法機制,雖然很便利,但沒有引入任何新的東西。我們可以使用點分語法來定義一個函數,然后用冒號語法調用它,反之亦然
環境變量
Lua語言將全局環境自身[1]保存在全局變量_G中(因此,_G._G與_G等價)。例如,如下代碼輸出了全局環境中所有全局變量的名稱:for n in pairs(_G)do print(n)end
_G _ENV
協程(Coroutine)
協程是一系列的可執行語句,擁有自己的棧、局部變量和指令指針,同時協程又與其他協程共享了全局變量和其他幾乎一切資源。線程與協程的主要區別在于,一個多線程程序可以并行運行多個線程,而協程卻需要彼此協作地運行,即在任意指定的時刻只能有一個協程運行,且只有當正在運行的協程顯式地要求被掛起(suspend)時其執行才會暫停。
Lua語言中協程相關的所有函數都被放在表coroutine中。
函數create用于創建新協程,該函數只有一個參數,即協程要執行的代碼的函數(協程體(body))。函數create返回一個"thread"類型的值,即新協程
local co= coroutine.create(function () print(“hello”) end)
print(coroutine.status(co))
當一個協程被創建時,它處于掛起狀態,即協程不會在被創建時自動運行。
協程的真正強大之處在于函數yield,該函數可以讓一個運行中的協程掛起自己,然后在后續恢復運行。
雖然協程的概念很容易理解,但涉及的細節其實很多。因此,對于那些已經對協程有一定了解的讀者來說,有必要在進行進一步學習前先理清一些細節。Lua語言提供的是所謂的非對稱協程(asymmetric coroutine),也就是說需要兩個函數來控制協程的執行,一個用于掛起協程的執行,另一個用于恢復協程的執行。而其他一些語言提供的是對稱協程(symmetric coroutine),只提供一個函數用于在一個協程和另一個協程之間切換控制權。
Lua JIT
Lua JIT是Lua語言的即時(JIT:Just-In-Time)編譯器,它提供基于快速解釋器和跟蹤編譯器的虛擬機,可顯著提高Lua程序的性能。
Luajit將原生Lua進行了擴展,使它支持JIT方式編譯運行,比起原生Lua程序,它有著如下特點:
JIT即時編譯器讓執行效率更高。
它同時兼容傳統的AOT編譯。
全新設計的Luajit字節碼文件格式,更加高效與更強的調試支持。(這一點在后面會著重介紹)
全新的Lua指令集。引入了中間表示IR,以及編譯引擎支持不同平臺的處理器指令即時編譯,完全的符合現代化編譯器設計,是編譯理論學習的絕佳好資料。