Lua 的語法比較簡單,學習起來也比較省力,但功能卻並不弱。
在Lua中,一切都是變量,除了關鍵字。請記住這句話。
I. 首先是註釋
寫一個程序,老是少不了註釋的。
在Lua中,你可使用單行註釋和多行註釋。
單行註釋中,連續兩個減號"--"表示註釋的開始,一直延續到行末爲止。至關於C++語言中的"//"。
多行註釋中,由"--[["表示註釋開始,而且一直延續到"]]"爲止。這種註釋至關於C語言中的"/…/"。在註釋當中,"[["和"]]"是能夠嵌套的。
II. Lua編程
經典的"Hello world"的程序老是被用來開始介紹一種語言。在Lua中,寫一個這樣的程序很簡單:
print("Hello world")
在Lua中,語句之間能夠用分號";"隔開,也能夠用空白隔開。通常來講,若是多個語句寫在同一行的話,建議老是用分號隔開。
Lua 有好幾種程序控制語句,如:
條件控制:if 條件 then … elseif 條件 then … else … end
While循環:while 條件 do … end
Repeat循環:repeat … until 條件
For循環:for 變量 = 初值,終點值,步進 do … end
For循環:for 變量1,變量2,… ,變量N in表或枚舉函數 do … end
注意一下,for的循環變量老是隻做用於for的局部變量,你也能夠省略步進值,這時候,for循環會使用1做爲步進值。
你能夠用break來停止一個循環。
若是你有程序設計的基礎,好比你學過Basic,C之類的,你會以爲Lua也不難。但Lua有幾個地方是明顯不一樣於這些程序設計語言的,因此請特別注意。
.語句塊
語句塊在C++中是用"{"和"}"括起來的,在Lua中,它是用do 和 end 括起來的。好比:
do print("Hello") end
你能夠在 函數 中和 語句塊 中定局部變量。
.賦值語句
賦值語句在Lua被強化了。它能夠同時給多個變量賦值。
例如:
a,b,c,d=1,2,3,4
甚至是:
a,b=b,a -- 多麼方便的交換變量功能啊。
在默認狀況下,變量老是認爲是全局的。假如你要定義局部變量,則在第一次賦值的時候,須要用local說明。好比:
local a,b,c = 1,2,3 -- a,b,c都是局部變量
.數值運算
和C語言同樣,支持 +, -, , /。但Lua還多了一個"^"。這表示指數乘方運算。好比2^3 結果爲8, 2^4結果爲16。
鏈接兩個字符串,能夠用".."運處符。如:
"This a " .. "string." -- 等於 "this a string"
.比較運算
< > <= >= == ~=
分別表示 小於,大於,不大於,不小於,相等,不相等
全部這些操做符老是返回true或false。
對於Table,Function和Userdata類型的數據,只有 == 和 ~=能夠用。相等表示兩個變量引用的是同一個數據。好比:
a={1,2}
b=a
print(a==b, a~=b) -- true, false
a={1,2}
b={1,2}
print(a==b, a~=b) -- false, true
.邏輯運算
and, or, not
其中,and 和 or 與C語言區別特別大。
在這裏,請先記住,在Lua中,只有false和nil才計算爲false,其它任何數據都計算爲true,0也是true!
and 和 or的運算結果不是true和false,而是和它的兩個操做數相關。
a and b:若是a爲false,則返回a;不然返回b
a or b:若是 a 爲true,則返回a;不然返回b
舉幾個例子:
print(4 and 5) --> 5
print(nil and 13) --> nil
print(false and 13) --> false
print(4 or 5) --> 4
print(false or 5) --> 5
在Lua中這是頗有用的特性,也是比較使人混洧的特性。
咱們能夠模擬C語言中的語句:x = a? b : c,在Lua中,能夠寫成:x = a and b or c。
最有用的語句是: x = x or v,它至關於:if not x then x = v end 。
.運算符優先級,從高到低順序以下:
^
not - (一元運算)
/
+ -
..(字符串鏈接)
< > <= >= ~= ==
and
or
III. 關鍵字
關鍵字是不能作爲變量的。Lua的關鍵字很少,就如下幾個:
and break do else elseif
end false for function if
in local nil not or
repeat return then true until while
IV. 變量類型
怎麼肯定一個變量是什麼類型的呢?你們能夠用type()函數來檢查。Lua支持的類型有如下幾種:
Nil 空值,全部沒有使用過的變量,都是nil。nil既是值,又是類型。
Boolean 布爾值
Number 數值,在Lua裏,數值至關於C語言的double
String 字符串,若是你願意的話,字符串是能夠包含'/0'字符的
Table 關係表類型,這個類型功能比較強大,咱們在後面慢慢說。
Function 函數類型,不要懷疑,函數也是一種類型,也就是說,全部的函數,它自己就是一個變量。
Userdata 嗯,這個類型專門用來和Lua的宿主打交道的。宿主一般是用C和C++來編寫的,在這種狀況下,Userdata能夠是宿主的任意數據類型,經常使用的有Struct和指針。
Thread 線程類型,在Lua中沒有真正的線程。Lua中能夠將一個函數分紅幾部份運行。若是感興趣的話,能夠去看看Lua的文檔。
V. 變量的定義
全部的語言,都要用到變量。在Lua中,無論你在什麼地方使用變量,都不須要聲明,而且全部的這些變量老是全局變量,除非,你在前面加上"local"。
這一點要特別注意,由於你可能想在函數裏使用局部變量,卻忘了用local來講明。
至於變量名字,它是大小寫相關的。也就是說,A和a是兩個不一樣的變量。
定義一個變量的方法就是賦值。"="操做就是用來賦值的
咱們一塊兒來定義幾種經常使用類型的變量吧。
A. Nil
正如前面所說的,沒有使用過的變量的值,都是Nil。有時候咱們也須要將一個變量清除,這時候,咱們能夠直接給變量賦以nil值。如:
var1=nil -- 請注意 nil 必定要小寫
B. Boolean
布爾值一般是用在進行條件判斷的時候。布爾值有兩種:true 和 false。在Lua中,只有false和nil才被計算爲false,而全部任何其它類型的值,都是true。好比0,空串等等,都是true。不要被C語言的習慣所誤導,0在Lua中的的確確是true。你也能夠直接給一個變量賦以Boolean類型的值,如:
varboolean = true
C. Number
在Lua中,是沒有整數類型的,也不須要。通常狀況下,只要數值不是很大(好比不超過100,000,000,000,000),是不會產生舍入偏差的。在不少CPU上,實數的運算並不比整數慢。
實數的表示方法,同C語言相似,如:
4 0.4 4.57e-3 0.3e12 5e+20
D. String
字符串,老是一種很是經常使用的高級類型。在Lua中,你能夠很是方便的定義很長很長的字符串。
字符串在Lua中有幾種方法來表示,最通用的方法,是用雙引號或單引號來括起一個字符串的,如:
"This is a string."
和C語言相同的,它支持一些轉義字符,列表以下:
/a bell
/b back space
/f form feed
/n newline
/r carriage return
/t horizontal tab
/v vertical tab
// backslash
/" double quote
/' single quote
/[ left square bracket
/] right square bracket
因爲這種字符串只能寫在一行中,所以,不可避免的要用到轉義字符。加入了轉義字符的串,看起來實在是不敢恭維,好比:
"one line/nnext line/n/"in quotes/", 'in quotes'"
一大堆的"/"符號讓人看起來很倒胃口。若是你與我有同感,那麼,咱們在Lua中,能夠用另外一種表示方法:用"[["和"]]"將多行的字符串括起來,如:
page = [[
<HTML>
<HEAD>
<TITLE>An HTML Page</TITLE>
</HEAD>
<BODY>
<A HREF="[ http://www.lua.org ]">Lua</A>
[[a text between double brackets]]
</BODY>
</HTML>
]]
值得注意的是,在這種字符串中,若是含有單獨使用的"[["或"]]"就仍然得用"/["或"/]"來避免歧義。固然,這種狀況是極少會發生的。
E. Table
關係表類型,這是一個很強大的類型。咱們能夠把這個類型看做是一個數組。只是C語言的數組,只能用正整數來做索引;在Lua中,你能夠用任意類型來做數組的索引,除了nil。一樣,在C語言中,數組的內容只容許一種類型;在Lua中,你也能夠用任意類型的值來做數組的內容,除了nil。
Table的定義很簡單,它的主要特徵是用"{"和"}"來括起一系列數據元素的。好比:
T1 = {} -- 定義一個空表
T1[1]=10 -- 而後咱們就能夠象C語言同樣來使用它了。
T1["John"]={Age=27, Gender="Male"}
這一句至關於:
T1["John"]={} -- 必須先定義成一個表,還記得未定義的變量是nil類型嗎
T1["John"]["Age"]=27
T1["John"]["Gender"]="Male"
當表的索引是字符串的時候,咱們能夠簡寫成:
T1.John={}
T1.John.Age=27
T1.John.Gender="Male"
或
T1.John{Age=27, Gender="Male"}
這是一個很強的特性。
在定義表的時候,咱們能夠把全部的數據內容一塊兒寫在"{"和"}"之間,這樣子是很是方便,並且很好看。好比,前面的T1的定義,咱們能夠這麼寫:
T1=
{
10, -- 至關於 [1] = 10
[100] = 40,
John= -- 若是你原意,你還能夠寫成:["John"] =
{
Age=27, -- 若是你原意,你還能夠寫成:["Age"] =27
Gender=Male -- 若是你原意,你還能夠寫成:["Gender"] =Male
},
20 -- 至關於 [2] = 20
}
看起來很漂亮,不是嗎?咱們在寫的時候,須要注意三點:
第一,全部元素之間,老是用逗號","隔開;
第二,全部索引值都須要用"["和"]"括起來;若是是字符串,還能夠去掉引號和中括號;
第三,若是不寫索引,則索引就會被認爲是數字,並按順序自動從1日後編;
表類型的構造是如此的方便,以至於經常被人用來代替配置文件。是的,不用懷疑,它比ini文件要漂亮,而且強大的多。
F. Function
函數,在Lua中,函數的定義也很簡單。典型的定義以下:
function add(a,b) -- add 是函數名字,a和b是參數名字
return a+b -- return 用來返回函數的運行結果
end
請注意,return語言必定要寫在end以前。假如你非要在中間放上一句return,那麼請寫成:do return end。
還記得前面說過,函數也是變量類型嗎?上面的函數定義,其實至關於:
add = function (a,b) return a+b end
當你從新給add賦值時,它就再也不表示這個函數了。你甚至能夠賦給add任意數據,包括nil (這樣,你就清除了add變量)。Function是否是很象C語言的函數指針呢?
和C語言同樣,Lua的函數能夠接受可變參數個數,它一樣是用"…"來定義的,好比:
function sum (a,b,…)
若是想取得…所表明的參數,能夠在函數中訪問arg局部變量(表類型)獲得。
如 sum(1,2,3,4)
則,在函數中,a = 1, b = 2, arg = {3, 4}
更難得的是,它能夠同時返回多個結果,好比:
function s()
return 1,2,3,4
end
a,b,c,d = s() -- 此時,a = 1, b = 2, c = 3, d = 4
前面說過,表類型能夠擁有任意類型的值,包括函數!所以,有一個很強大的特性是,擁有函數的表,哦,我想更恰當的應該說是對象吧。Lua可使用面向對象編程了。不信?那我舉例以下:
t =
{
Age = 27
add = function(self, n) self.Age = self.Age+n end
}
print(t.Age) -- 27
t.add(t, 10)
print(t.Age) -- 37
不過,t.add(t,10) 這一句實在是有點土對吧?不要緊,在Lua中,你能夠簡寫成:
t:add(10) -- 至關於 t.add(t,10)
G. Userdata 和 Thread
這兩個類型的話題,超出了本文的內容,就不打算細說了。 程序員
1 - 緒論
Lua是一種爲支持有數據描述機制的通常過程式編程語言而設計的擴展編程語言。它一樣能夠對面向對象語言、函數式程序設計(Functional Programming,如Lisp)以及數據驅動編程(data-driven programming)提供很好的支持。它的目標是被用做一種強大的、輕型的配置語言。Lua目前已經被實現爲一個擴展庫,是用clean C (ANSI C/C++的一個通用子集)編寫的。
做爲一個擴展語言,Lua沒有"Main"函數的概念:它僅僅是嵌入一個宿主程序進行工做,能夠稱之爲 嵌入式編程 或者簡單的說是 宿主編程。這個宿主程序能夠調用函數來執行Lua的代碼片段,能夠設置和讀取Lua的變量,能夠註冊C函數讓Lua代碼調用。Lua的能力能夠擴展到更大範圍,在不一樣的領域內,這樣就在一樣的語法框架下建立了你自定義的編程語言。
Lua的發行版包括一個獨立的嵌入式程序,lua,他使用Lua的擴展庫來提供一個徹底的Lua解釋器。
Lua是自由軟件,一般不提供任何擔保,如它的版權說明中敘述的那樣。 手冊中描述的實如今Lua的官方網站能夠找到,[ http://www.lua.org ]。
若是須要知道Lua設計背後的一些決定和討論,能夠參考如下論文,它們均可以在Lua的網站上找到。
R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. Lua---an extensible extension language. Software: Practice & Experience 26 #6 (1996) 635-652.
L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. The design and implementation of a language for extending applications. Proceedings of XXI Brazilian Seminar on Software and Hardware (1994) 273-283.
L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. Lua: an extensible embedded language. Dr. Dobb's Journal 21 #12 (Dec 1996) 26-33.
R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. The evolution of an extension language: a history of Lua, Proceedings of V Brazilian Symposium on Programming Languages (2001) B-14-B-28.
Lua在葡萄牙語中的意思是「月亮」,發音是 LOO-ah。
2 - 語言
這一章將描述Lua的詞法、語法和語義結構。換句話說,這一章會講什麼標記是合法的,他們是如何組合的,以及他們的組合是什麼含義。
語言結構會使用經常使用的擴展BNF範式來解釋,如{a} 表示0或多個a, [a] 表示a是可選的(0個或1個)。非終端字體(不能顯示的)用 斜體表示,關鍵字是粗體,其餘終端符號用typewriter(等寬)字體,並用單引號引出。
2.1 - 詞法約定
Lua中的標識符(Identifiers)能夠是任意的數字、字符和下劃線「_」,但不能以數字開頭。這條規則符合大多數編程語言中的標識符的定義。(字符的具體定義要根據系統的地區設置:任何區域設置能夠認同的字母表中的字母均可以用在標識符中。)
下面的關鍵字(keywords)爲保留關鍵字不能夠做爲標識符出現:
and break do else elseif
end false for function if
in local nil not or
repeat return then true until while
Lua對大小寫敏感:and是一個保留字,可是 And 和 AND 是兩個不同的、但都合法的標識符。習慣上來講,如下劃線開始且後面跟着大寫字母的標識符 (例如 _VERSION) 是爲Lua內部變量所保留的。
下面的字符(串)是其餘的一些標記:
+ - / ^ =
~= <= >= < > ==
( ) { } [ ]
; : , . .. ...
字符串(Literal strings) 以單引號或者雙引號定界,同時能夠包含如下C語言風格的轉義字符:
/a --- 鈴聲(bell)
/b --- 回退(backspace)
/f --- form feed
/n --- 新行(newline)
/r --- 回車(carriage return)
/t --- 水平製表符(horizontal tab)
/v --- 垂直製表符(vertical tab)
// --- 反斜槓(backslash)
/" --- 雙引號(quotation mark)
/' --- 單引號(apostrophe)
/[ --- 左方括號(left square bracket)
/] --- 右方括號(right square bracket)
另外,一個 `/newline´ (一個反斜槓加上一個真正的換行符)會致使字符串內的分行。字符串中的字符也可使用轉義字符`/ddd´經過數字值來指定。ddd 是最多爲3個十進制數字的序列。Lua中的字符串也能夠包含8進制數字,包括嵌入零,它能夠表示爲 `/0´。
字符串也能夠用雙方括號來定界[[ · · · ]]。這種括號方式的語法,字符串能夠跨越多行,也能夠包含嵌套的,同時不會轉義任何序列。方便起見,當開始的 `[[´ 後面緊跟着一個換行符的話,這個換行符不會包括在字符串內。舉個例子:在一個使用ASCII編碼(其中`a´ 的編碼是 97,換行符是 10,字符`1´ 是 49)的系統中,如下四種格式獲得的都是同一個字符串:
(1) "alo/n123/""
(2) '/97lo/10/04923"'
(3) [[alo
123"]]
(4) [[
alo
123"]]
數值常量(Numerical constants) 能夠有一個可選的底數部分和一個可選的指數部分。如下是有效的數值常量:
3 3.0 3.1416 314.16e-2 0.31416E1
註釋(Comments) 能夠在任何地方出現,必須在最前面加上雙減號 (--)。若是緊接着 -- 的文本不是 [[,那麼會認爲是一個 短註釋(short comment), 這一行日後到行尾都是註釋。不然,會認爲是一個 常註釋(long comment),註釋直到相應的 ]]結束。長註釋能夠跨越多行,同時能夠包含嵌套的 [[ · · · ]] 括號對。
爲了方便起見,文件的第一行若是是以#開始,這個機制容許Lua在Unix系統中用作一個腳本解釋器(見 6)。
2.2 - 值和類型
Lua是一種 動態類型語言(dynamically typed language)。這意味着變量是沒有類型的;只有值纔有。語言中沒有類型定義。全部的值都包含他自身的類型。
Lua中有八種基本類型:nil, boolean, number, string, function, userdata, thread 和 table。 Nil 空類型只對應 nil值,他的屬性和其餘任何值都有區別;一般它表明沒有有效的值。 Boolean 布爾類型有兩種不一樣的值 false and true。在Lua中, nil and false 表明成假條件;其餘任何值都表明成真條件。 Number 數字類型表示實數(雙精度浮點數)。(構建Lua解釋器時也能夠很容易地用其餘內部的表示方式表示數字,如單精度浮點數或者長整型)。 String 字符串類型表示一個字符的序列。Lua 字符串能夠包含8位字符,包括嵌入的 ('/0') (見 2.1)。
函數是Lua中的 第一類值(first-class values)。也就是說函數能夠保存在變量中,看成參數傳遞給其餘函數,或者被看成結果返回。Lua能夠調用(和處理)Lua寫的函數和C寫的函數 (見 2.5.7)。
用戶數據類型(userdata) 提供了讓任意C數據儲存在Lua變量中的功能。這種類型直接對應着一塊內存,Lua中也沒有任何預先定義的操做,除了賦值和一致性比較。然而,經過使用 元表(metatables),程序員能夠定義處理userdata的操做。(見 2.8)。 Userdata 值不能在Lua中創建或者修改,只能經過 C API。這保證了宿主程序的數據完整性。
線程(thread) 類型表明了相互獨立的執行線程,用來實現同步程序。
表(table) 類型實現了聯合數組,也就是說,數組不只可使用數字,還能使用其餘的值(除了 nil)。 並且,tables 能夠是 互異的(heterogeneous),他們能夠保存任何類型的值(除了 nil)。 Tables 是Lua中惟一的數據結構機制;他們能夠用來表示通常數組,特徵表,集合,記錄,圖,樹等等。若是要表示記錄,Lua使用字段名做爲索引。語言支持 a.name 這種比較優美的表示方式,還有 a["name"]。在Lua中有幾種創建表的簡便方法 (見 2.5.6)。
就像索引同樣,表字段的值也能夠是任何類型(除了 nil)。特別須要注意地是,因爲函數是第一型的值,表字段也能夠包含函數。這樣表也能夠支持 方法(methods) (見 2.5.8)。
表,函數,和用戶數據類型的值都是 對象(objects):變量不會包含他們的實際值,只是一個他們的引用(references)。 賦值,參數傳遞和函數返回只是操做這些值的引用,這些操做不會暗含任何拷貝。
庫函數 type 返回一個字符串描述給出值所表示的類型 (見 5.1)。
2.2.1 - 類型轉換
Lua提供運行時的數字和字符串值得自動轉換。任何對字符串的算術操做都會現嘗試把字符串轉換成數字,使用通常規則轉換。反過來,當一個數值用在須要字符串的地方時,數字會自動轉換成字符串,遵循一種合理的格式。若是要指定數值如何轉換成字符串,請使用字符串庫中的 format 函數(見 5.3)。
2.3 - 變量
變量是儲存值的地方。Lua中有三種不一樣的變量:全局變量,局部變量和表字段。
一個名稱能夠表示全局變量或局部變量(或者一個函數的正式參數,一種局部變量的特殊形式):
var ::= Name
Lua假設變量是全局變量,除非明確地用local進行聲明 (見 2.4.7)。局部變量有 詞義範圍(lexically scoped):局部變量能夠被在它們範圍內的函數自由訪問 (見 2.6)。
在變量第一次賦值以前,它的值是 nil。
方括號用於對錶進行檢索:
var ::= prefixexp `[´ exp `]´
第一個表達式 (prefixexp)結果必須是表;第二個表達式 (exp) 識別表中一個特定條目。給出表的表達式有一個限制語法;詳細見 2.5。
var.NAME 語法是 var["NAME"] 的較好形式:
var ::= prefixexp `.´ Name
訪問全局變量和表字段的實質能夠經過元表進行改變。對索引變量 t[i] 的訪問等同於調用 gettable_event(t,i)。(關於 gettable_event 的完整描述見 2.8。這個函數並無在Lua中定義,也沒法調用。咱們在這裏僅僅用來解釋原理)。
全部的全局變量存在一個普通的Lua表中,稱之爲 環境變量表(environment tables) 或簡稱 環境(environments)。由C寫的並導入到Lua中的函數 (C 函數) 所有共享一個通用 全局環境(global environment)。Lua寫的每一個函數 (a Lua 函數) 都有一個它本身的環境的引用,這樣這個函數中的全部的全局變量都會指向這個環境變量表。當新建立一個函數時,它會繼承建立它的函數的環境。要改變或者得到Lua函數的環境表,能夠調用 setfenv or getfenv (見 5.1)。
訪問全局變量 x 等同於 _env.x,又等同於
gettable_event(_env, "x")
_env 是運行的函數的環境。(_env 變量並無在Lua中定義。咱們這裏僅僅用來解釋原理)
2.4 - 語句
Lua支持一種很通俗的語句集,和Pascal或者C中的很類似。他包括賦值,控制結構,過程調用,表構造和變量聲明。
2.4.1 - 語句段
Lua執行的最小單元稱之爲一個 段(chunk)。一段語句就是簡單的語句的序列,以順序執行。每個語句後面均可以加上一個分號(可選):
chunk ::= {stat [`;´]}
Lua將語句段做爲一個匿名函數 (見 2.5.8) 的本體進行處理。這樣,語句段能夠定義局部變量或者返回值。
一段語句能夠儲存在文件內或者宿主程序的一個字符串中。當語句段被執行時,他首先被預編譯成虛擬機使用的字節碼,而後虛擬機用一個解釋器執行被編譯的代碼。
語句段也能夠被預編譯爲二進制代碼;詳情參看 luac 程序。源代碼和編譯形態能夠互相轉換;Lua自動監測文件類型而後做相應操做。
2.4.2 - 語句塊
一個語句塊是一系列語句;從語句構成上來看,語句塊等同於語句段:
block ::= chunk
一個語句塊能夠明肯定界來替換單個語句:
stat ::= do block end
顯式語句塊能夠很好地控制變量的聲明範圍。顯示語句塊有時也常會在另外一個語句塊的中間添加 return 或 break 語句 (見 2.4.4)。
2.4.3 - 賦值
Lua容許多重賦值。所以,賦值的語法定義爲:等號左邊是一個變量表,右邊是一個表達式表。兩邊的表中的元素都用逗號分隔開來:
stat ::= varlist1 `=´ explist1
varlist1 ::= var {`,´ var}
explist1 ::= exp {`,´ exp}
咱們將在 2.5 討論表達式。
在賦值以前,值的表長度會被 調整 爲和變量的表同樣。若是值比須要的多,多出的值就會被扔掉。若是值的數量不夠,就會用足夠多的 nil 來填充表直到知足數量要求。若是表達式表以一個函數調用結束,那麼在賦值以前,函數返回的全部的值都會添加到值的表中(除非把函數調用放在括號裏面;見 2.5)。
賦值語句首先計算出全部的表達式,而後纔會執行賦值,因此代碼:
i = 3
i, a[i] = i+1, 20
設置 a[3] 爲 20,但不影響 a[4]。由於在 a[i] 中的 i 在賦值爲4以前是等於3。一樣的,下面這行:
x, y = y, x
能夠交換 x 和 y 的值。
對全局變量和表字段的賦值能夠看做是經過元表進行的。對一個索引變量的賦值 t[i] = val 等同於 settable_event(t,i,val)。 (settable_event詳細介紹參看 2.8 ,Lua中並未定義該函數,他也沒法直接調用。咱們這裏只是用它來進行解釋。)
對全局變量的賦值 x = val 等同於賦值語句 _env.x = val,像前面也等同於:
settable_event(_env, "x", val)
_env 是運行函數的環境。(_env 變量並未在Lua中定義。咱們這裏只是用來進行解釋。)
2.4.4 - 控制結構
控制結構 if, while 和 repeat 具備通用的含義和相似的語法:
stat ::= while exp do block end
stat ::= repeat block until exp
stat ::= if exp then block {elseif exp then block} [else block] end
Lua也有 for 語句,有兩種格式 (見 2.4.5)。
控制結構的條件表達式 exp 能夠返回任意值。false 和 nil 都表示假。全部其餘的值都認爲是真(特別要說明的:數字0和空字符串也表示真)。
語句 return 用來從函數或者是語句段中返回一個值。函數和語句段均可以返回多個值,因此 return 語句的語法爲:
stat ::= return [explist1]
break 語句能夠用來終止while, repeat 或者 for 循環的執行,直接跳到循環後面的語句。
stat ::= break
break 結束最裏面的一個循環。
因爲語法的緣由, return 和 break 語句只能做爲語句塊的 最後一個 語句。若是確實須要在語句塊的中間使用 return 或者 break,須要使用一個顯示語句塊: `do return end´ 和 `do break end´,這樣如今 return 和 break 就成爲他們(內部)語句塊中的最後一個語句了。實際上,這兩種用法通常只用在調試中。
2.4.5 - For 語句
for 語句有兩種形式:數值形式和通常形式。
數值形式的 for 循環根據一個控制變量用算術過程重複一語句塊。語法以下:
stat ::= for Name `=´ exp `,´ exp [`,´ exp] do block end
block 語句塊根據 name 以第一個 exp 的值開始,直到他以第三個 exp 爲步長達到了第二個 exp。一個這樣的 for 語句:
for var = e1, e2, e3 do block end
等價於一下代碼:
do
local var, _limit, _step = tonumber(e1), tonumber(e2), tonumber(e3)
if not (var and _limit and _step) then error() end
while (_step>0 and var<=_limit) or (_step<=0 and var>=_limit) do
block
var = var + _step
end
end
注意:
三種控制表達式只會被計算一次,在循環開始以前。他們的結果必須是數值。
_limit 和 _step 是不可見的變量。這裏只是爲了進行解釋。
若是你在程序塊內給 var 賦值,結果行爲將會不肯定。
若是沒有給出第三個表達式(步長),那麼默認爲1。
你可使用 break 來退出 for 循環。
循環變量 var 是局部變量;你不能夠在 for 循環結束以後繼續使用。若是你須要使用這個值,請在退出循環以前把它們傳給其餘變量。
for 的語句的通常形式是操做於函數之上的,稱之爲迭代器(iterators)。每個迭代過程,它調用迭代函數來產生新的值,直到新的值是 nil 。通常形式 for 循環有以下語法:
stat ::= for Name {`,´ Name} in explist1 do block end
一個這樣的 for 語句
for var_1, ..., var_n in explist do block end
等同於如下代碼:
do
local _f, _s, var_1 = explist
local var_2, ... , var_n
while true do
var_1, ..., var_n = _f(_s, var_1)
if var_1 == nil then break end
block
end
end
注意:
explist 只會計算一次。他的結果是一個 迭代 函數,一個 狀態,和給第一個 迭代變量的一個初始值。
_f 和 _s 是不可見的變量。這裏只是用來進行解釋說明。
若是你在語句塊中給 var_1 賦值,那麼行爲就會變得不肯定。
你可使用 break 來退出 for 循環。
循環變量 var_i 是局部變量;你不能夠在 for 循環結束以後繼續使用。若是你須要使用這個值,請在退出循環以前把它們傳給其餘變量。
2.4.6 - 語句式函數調用
若是要忽略可能的影響,函數調用能夠按照語句執行:
stat ::= functioncall
I在這裏,全部的返回值都會被忽略。函數調用將在 2.5.7 詳細解釋。
2.4.7 - 局部變量聲明
局部變量能夠在語句塊中任何地方聲明。聲明時也能夠添加一個初始賦值:
stat ::= local namelist [`=´ explist1]
namelist ::= Name {`,´ Name}
若是出現初始賦值,他的語法和多重賦值語句同樣(見 2.4.3)。不然,全部的變量都會初始化爲 nil。
一個語句段也是一個語句塊(見 2.4.1),因此語句段以內的任何顯式語句塊以外也能夠聲明局部變量。這種局部變量在語句段結束就會銷燬。
局部變量的可見規則會在 2.6解釋。
2.5 - 表達式
Lua中有如下幾種基本表達式:
exp ::= prefixexp
exp ::= nil | false | true
exp ::= Number
exp ::= Literal
exp ::= function
exp ::= tableconstructor
prefixexp ::= var | functioncall | `(´ exp `)´
數字和字符串已經在 2.1 中解釋;變量在 2.3 中解釋;函數定義在 2.5.8;函數調用在 2.5.7;表構造器在 2.5.6。
一個用括號括起的表達式只會返回一個值。這樣,(f(x,y,z)) 將只會返回單一的一個值,即便 f 能夠返回多個值,((f(x,y,z)) 的值將是 f 返回的第一個值或者若是 f 沒有返回任何值就是 nil )。
表達式也可使用各類算術運算符,關係運算符和邏輯運算符,下面幾節就會講到。
2.5.1 - 算術運算符
Lua支持常見的幾種運算符:二元 + (加), - (減), (乘), / (除), 以及 ^ (指數運算); 一元 - (負號)。若是操做數是數字,或者是能夠轉換成數字的字符串(見 2.2.1),那麼全部的操做都和算術意義上的運算一致(除了指數)。指數運算實際上是調用一個全局函數 __pow,不然一個合適的元方法將會被調用(見 2.8)。標準數學庫定義了函數 __pow,給出了指數運算的定義(見 5.5)。
2.5.2 - 關係運算符
Lua中的關係運算符有
== ~= < > <= >=
這些運算只會產生 false 或 true值。
等於 (==) 先比較操做數的類型。若是類型不同,結果即是 false。不然,再比較操做數的值。對象(表,用戶數據,線程,和函數)是按照引用進行比較:只有兩個對象是同一個對象的時候,才認爲是相等。每次你建立一個新的對象(表,用戶數據,或者是函數)。這個新的對象將不一樣於前面存在的任何對象。
你能夠用"eq"元方法改變Lua比較表的方式(見 2.8)。
2.2.1 的轉換規則 不適用 於相等比較。這樣," "0"==0 結果是 false ,一樣 t[0] 和 t["0"] 給出的是表中不一樣的字段。
而操做符 ~= 是等於 (==) 的相反的操做。
T操做符的執行順序以下。若是兩個參數都是數字,那麼它們就直接進行比較。若是,兩個參數都是字符串,那麼它們的值會根據當前的區域設置進行比較。不然,Lua嘗試調用"lt"或者 "le" 元方法(見 2.8)。 shell
2.5.3 - 邏輯運算符
Lua中的邏輯運算符是:
and or not
和控制結構同樣(見 2.4.4),全部的邏輯操做符認爲 false 和 nil 都是假,其餘的值都是真。
not 操做符老是返回 false 或 true。
合取運算 and 若是第一個參數是 false 或者 nil 則返回第一個參數;不然 and 返回第二個參數。析取運算 or 若是第一個參數不是 nil 或 false 則返回第一個參數,不然 or 返回第二個參數。 and 和 or 都使用截取計算,也就是,只有有必要的狀況下才計算第二個參數。例如:
10 or error() -> 10
nil or "a" -> "a"
nil and 10 -> nil
false and error() -> false
false and nil -> false
false or nil -> nil
10 and 20 -> 20
2.5.4 - 串聯接
在Lua中字符串鏈接操做符是兩個點 (`..´)。若是兩邊的操做數都是字符或者數字,他們就都會按照 2.2.1的規則被轉換成字符串。不然,將調用 "concat" 元方法(見 2.8)。
2.5.5 - 優先級
Lua中的操做符的優先級以下表所示,從低到高優先級:
or
and
< > <= >= ~= ==
..
+ -
/
not - (unary)
^
表達式中,你可使用括號來改變優先順序。串聯接符 (`..´) 和指數符 (`^´) 都是右結合的。其餘二元操做都是左結合的。
2.5.6 - 表構造器
表構造器是建立表的表達式。當計算構造器的時候,就會建立一個新的表。構造器能夠用來建立空的表,或者建立表並初始化一些字段。通常的語法以下:
tableconstructor ::= `{´ [fieldlist] `}´
fieldlist ::= field {fieldsep field} [fieldsep]
field ::= `[´ exp `]´ `=´ exp | Name `=´ exp | exp
fieldsep ::= `,´ | `;´
[exp1] = exp2 形式的每個添加到新表中的字段條目以 exp1 爲鍵並以 exp2 爲值。name = exp 形式的字段,等同於 ["name"] = exp。最後,exp 形式的字段等同於 [i] = exp 其中 i 是連續的整數,從1開始。其它格式的字段不會影響它的計數。例如:
a = {[f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45}
等同於:
do
local temp = {}
temp[f(1)] = g
temp[1] = "x" -- 1st exp
temp[2] = "y" -- 2nd exp
temp.x = 1 -- temp["x"] = 1
temp[3] = f(x) -- 3rd exp
temp[30] = 23
temp[4] = 45 -- 4th exp
a = temp
end
若是列表中最後一個字段的形式是 exp 同時表達式又是一個函數調用,那麼調用返回的全部值會依次進入列表(見 2.5.7)。若是要避免這種狀況,在函數調用兩邊加上括號(見 2.5)。
字段列表能夠有一個結尾的分隔符,這個對由機器生成的列表十分方便。
2.5.7 - 函數調用
Lua中的一個函數調用有以下語法:
functioncall ::= prefixexp args
在函數調用中,首先會計算 prefixexp 和 args 。若是 prefixexp 的值是 function 類型,那麼那個函數就會被調用,同時使用給出的參數。不然,他的 "call" 元方法就會被調用,第一個參數是 prefixexp 的值,接下來是原來的調用參數(見 2.8)。
形式
functioncall ::= prefixexp `:´ Name args
能夠用來調用「方法」("methods")。調用 v:name(...) 語法上比 v.name(v,...),要好一些,除非表達式 v 只計算一次。
參數能夠有如下幾種語法:
args ::= `(´ [explist1] `)´
args ::= tableconstructor
args ::= Literal
全部的參數表達式都會在實際調用以前進行計算。f{...} 的調用形式在語法上較 f({...}) 要好,是由於,參數列表示一個單獨的新表。 f'...' (或者 f"..." 或者 f[[...]]) 較 f('...') 要好,是由於參數列表是一個單獨的字符串。
由於函數能夠返回任意個結果(見 2.4.4),結果的數量必須在使用它們前進行調整。若是函數按照語句進行調用(見 2.4.6),那麼它的返回列表就會被調整爲零個元素,這樣就捨棄了全部的返回值。若是調用函數時,他是一個表達式列表的最後一個元素,那麼不會作調整(除非調用時加了括號)。
如下是一些例子:
f() -- 調整爲0個結果
g(f(), x) -- f() 被調整成1個結果
g(x, f()) -- g 得到 x 加上f()返回的全部值
a,b,c = f(), x -- f() 被調整成1個結果(此時c得到nil值)
a,b,c = x, f() -- f() 被調整爲兩個結果
a,b,c = f() -- f() 被調整爲3個結果
return f() -- 返回全部 f() 返回的值
return x,y,f() -- 創建一個表包含全部 f() 返回的值
{f()} -- creates a list with all values returned by f()
{f(), nil} -- f() 被調整爲一個結果
若是你用括號括起調用的函數,那麼它就會被調整爲返回一個值。
return x,y,(f()) -- returns x, y, and the first value from f()
{(f())} -- creates a table with exactly one element
做爲Lua語法自由格式的一個例外,你不能在函數調用的 `(´ 前面加入一個換行。這個限制能夠避免語言中的一些二義性。若是你寫:
a = f
(g).x(a)
Lua會讀做 a = f(g).x(a)。這樣,若是你想執行爲兩條語句,你必須在中間加分號。若是你實際上想調用 f,你就必須刪除 (g) 前面的換行。
return functioncall 的調用格式稱之爲 尾部調用(tail call)。Lua實現了proper tail calls;在一個尾部調用中,被調用的函數將會從新使用調用程序的棧。所以,程序執行對嵌套尾部調用的次數沒有任何限制。然而,尾部調用會清楚調用函數的調試信息。注意尾部調用只有在特殊的語法中才能出現,也就是 return 只有一個函數調用做爲參數,這種語法保證了調用函數確切返回被調用函數的返回值。因此,下面的例子都不是尾部調用:
return (f(x)) -- results adjusted to 1
return 2 f(x)
return x, f(x) -- additional results
f(x); return -- results discarded
return x or f(x) -- results adjusted to 1
2.5.8 - 函數定義
函數定義的語法是:
function ::= function funcbody
funcbody ::= `(´ [parlist1] `)´ block end
下面較好的語法簡化了函數定義:
stat ::= function funcname funcbody
stat ::= local function Name funcbody
funcname ::= Name {`.´ Name} [`:´ Name]
語句
function f () ... end
會被翻譯爲
f = function () ... end
語句
function t.a.b.c.f () ... end
會被翻譯爲
t.a.b.c.f = function () ... end
語句
local function f () ... end
會被翻譯爲
local f; f = function () ... end
一個函數定義是一個可執行的表達式,他的類型爲 函數(function) 。當Lua預編譯語句段的時候,他的函數體也會被預編譯。這樣,當Lua執行函數定義的時候,函數被 實例化 (封裝 closed)。這個函數實例(或閉包 closure)是表達式的最終結果。同一個函數的不一樣的實例能夠引用不一樣的外部局部變量也能夠有不一樣的環境表。
形式參數(表明參數的變量,簡稱形參)就像用實際參數值(簡稱實參)初始化的局部變量同樣。
parlist1 ::= namelist [`,´ `...´]
parlist1 ::= `...´
當調用一個函數時,實參表會調整爲和形參同樣的長度,除非函數是 variadic 或者 變長參數函數(vararg function)。變長參數函數在其參數列表最後有三個點 (`...´)。 變長參數函數不會對參數列表進行調整;而是,它把全部的額外實參放到一個隱含的形參 arg中。 arg 的值是一個表,包含一個字段 `n´ 表示額外參數的個數,位置 1, 2, ..., n是額外的參數。
請思考如下函數定義的例子:
function f(a, b) end
function g(a, b, ...) end
function r() return 1,2,3 end
而後,咱們有如下實參到形參的對應關係:
CALL PARAMETERS
f(3) a=3, b=nil
f(3, 4) a=3, b=4
f(3, 4, 5) a=3, b=4
f(r(), 10) a=1, b=10
f(r()) a=1, b=2
g(3) a=3, b=nil, arg={n=0}
g(3, 4) a=3, b=4, arg={n=0}
g(3, 4, 5, 8) a=3, b=4, arg={5, 8; n=2}
g(5, r()) a=5, b=1, arg={2, 3; n=2}
結果使用 return 語句返回(見 2.4.4)。若是控制到達了函數尾部而沒有遇到 return 語句,那麼函數沒有返回值。
冒號(:) 語法是用來定義 methods 的,也就是,函數有一個隱含的額外參數 self. 。這樣,語句:
function t.a.b.c:f (...) ... end
相對如下是較好的形式:
t.a.b.c.f = function (self, ...) ... end
2.6 - 可見性規則
Lua是一個有詞法範圍的語言。變量的範圍從聲明語句後的第一個語句開始到包含聲明的最內部的語句塊爲止。例如:
x = 10 -- global variable
do -- new block
local x = x -- new `x', with value 10
print(x) --> 10
x = x+1
do -- another block
local x = x+1 -- another `x'
print(x) --> 12
end
print(x) --> 11
end
print(x) --> 10 (the global one)
注意:在相似 local x = x,正在聲明的新的 x 還沒有進入範圍,因此第二個 x 指代的是外面的變量。
因爲詞法範圍的規則,在局部變量的範圍內定義的函數能夠任意訪問這些變量。例如:
local counter = 0
function inc (x)
counter = counter + x
return counter
end
內部函數使用的局部變量在函數內部稱之爲 上值(upvalue),或者 外局部變量(external local variable)。
注意每一個 local 語句執行時會定義一個新的局部變量。看如下例子:
a = {}
local x = 20
for i=1,10 do
local y = 0
a[i] = function () y=y+1; return x+y end
end
循環產生了十個閉包(也就是,十個匿名函數的實例)。每一個閉包使用不一樣的 y 變量,但他們共享同一個 x 變量。
2.7 - 錯誤處理
由於Lua是一個擴展語言,全部的Lua動做都是從宿主程序中調用Lua庫中函數的C代碼開始的(見 3.15)。不管錯誤發生在Lua編譯過程時或執行時,控制返回C,而後能夠作相應的處理(好比打印一個錯誤)。
Lua代碼能夠經過調用error函數來產生一個錯誤(見 5.1)。若是你要在Lua中捕獲錯誤,你可使用 pcall 函數(見 5.1)。
2.8 - 元表 (Metatables)
Lua中的每個表和用戶數據均可以擁有一個 元表(metatable)。這個 元表 是一個普通的Lua表,定義了在特定操做下原始表和用戶數據的行爲。你能夠經過設置一個對象的元表中的特定字段來更改它某些方面的行爲。例如,當一個對象是一個加法的操做數時,Lua檢查它的元表中的 "__add" 字段是否是一個函數。若是是,Lua調用它來執行加法。
咱們稱元表中的鍵(字段名,key)爲 事件(events) ,值爲 元方法(metamethods)。在上一個例子中, "add" 是事件,執行加法的函數是元方法。
你能夠經過 set/getmetatable 函數來查詢和更改一個對象的元表(見 5.1)。
元表能夠控制對象在算術操做、比較、串鏈接、索引取值中如何運行。元表也能夠定義一個函數當收集內存垃圾時調用。每個操做這裏Lua都用一個特定的鍵關聯,稱之爲事件。當Lua對一個表或是一個用戶數據執行上面中的一個操做時,它先檢查元表控制的操做已經羅列在下面。每一個操做有一個相應的名稱,表明了他的含義。他們在元表中的鍵是由名稱前加上兩條下劃線;如,操做 "add" 的鍵是 "__add"。這些操做的語義
這裏給出的Lua代碼僅僅是說明性的;真正的行爲是硬編碼在解釋器中的,比下面的的模擬的效率要高不少。描述中用到的函數 (rawget, tonumber, 等等) 在 5.1 中會對他們進行描述。特別地,要得到一個給定對象的元方法,咱們使用這個表達式:
metatable(obj)[event]
這個要讀做:
rawget(metatable(obj) or {}, event)
也就是,訪問元方法時不會調用其它元方法,同時調用沒有元表的對象不會出錯(它返回一個 nil值)。
"add": + 加法操做。
下面的 getbinhandler 函數定義了Lua如何給一個二元操做選擇一個處理器。首先,Lua嘗試第一個操做數。若是它的類型沒有定義這個操做的處理器,那麼而後Lua嘗試第二個操做數。
function getbinhandler (op1, op2, event)
return metatable(op1)[event] or metatable(op2)[event]
end
利用該函數,op1 + op2 的行爲方式可看做是
function add_event (op1, op2)
local o1, o2 = tonumber(op1), tonumber(op2)
if o1 and o2 then -- both operands are numeric?
return o1 + o2 -- `+' here is the primitive `add'
else -- at least one of the operands is not numeric
local h = getbinhandler(op1, op2, "__add")
if h then
-- call the handler with both operands
return h(op1, op2)
else -- no handler available: default behavior
error("...")
end
end
end
"sub": - 操做。行爲方式相似 "add" 操做。
"mul": 操做。行爲方式相似 "add" 操做。
"div": / 操做。行爲方式相似 "add" 操做。
"pow": ^ (指數) 操做
function pow_event (op1, op2)
local o1, o2 = tonumber(op1), tonumber(op2)
if o1 and o2 then -- both operands are numeric?
return __pow(o1, o2) -- call global `__pow'
else -- at least one of the operands is not numeric
local h = getbinhandler(op1, op2, "__pow")
if h then
-- call the handler with both operands
return h(op1, op2)
else -- no handler available: default behavior
error("...")
end
end
end
"unm": 一元取負 - 操做。
function unm_event (op)
local o = tonumber(op)
if o then -- operand is numeric?
return -o -- `-' here is the primitive `unm'
else -- the operand is not numeric.
-- Try to get a handler from the operand
local h = metatable(op).__unm
if h then
-- call the handler with the operand and nil
return h(op, nil)
else -- no handler available: default behavior
error("...")
end
end
end
"concat": .. (串鏈接)操做。
function concat_event (op1, op2)
if (type(op1) == "string" or type(op1) == "number") and
(type(op2) == "string" or type(op2) == "number") then
return op1 .. op2 -- primitive string concatenation
else
local h = getbinhandler(op1, op2, "__concat")
if h then
return h(op1, op2)
else
error("...")
end
end
end
"eq": == 操做。函數 getcomphandler 定義了Lua是如何爲比較操做選擇一個元方法的。只有當參與比較的兩個對象屬於同一類型並且須要的元方法同樣時,纔會選擇這個元方法。
function getcomphandler (op1, op2, event)
if type(op1) ~= type(op2) then return nil end
local mm1 = metatable(op1)[event]
local mm2 = metatable(op2)[event]
if mm1 == mm2 then return mm1 else return nil end
end
事件以下定義:
function eq_event (op1, op2)
if type(op1) ~= type(op2) then -- different types?
return false -- different objects
end
if op1 == op2 then -- primitive equal?
return true -- objects are equal
end
-- try metamethod
local h = getcomphandler(op1, op2, "__eq")
if h then
return h(op1, op2)
else
return false
end
end
a ~= b is equivalent to not (a == b).
"lt": < 操做。
function lt_event (op1, op2)
if type(op1) == "number" and type(op2) == "number" then
return op1 < op2 -- numeric comparison
elseif type(op1) == "string" and type(op2) == "string" then
return op1 < op2 -- lexicographic comparison
else
local h = getcomphandler(op1, op2, "__lt")
if h then
return h(op1, op2)
else
error("...");
end
end
end
a > b is equivalent to b < a.
"le": <= 操做。
function le_event (op1, op2)
if type(op1) == "number" and type(op2) == "number" then
return op1 <= op2 -- numeric comparison
elseif type(op1) == "string" and type(op2) == "string" then
return op1 <= op2 -- lexicographic comparison
else
local h = getcomphandler(op1, op2, "__le")
if h then
return h(op1, op2)
else
h = getcomphandler(op1, op2, "__lt")
if h then
return not h(op2, op1)
else
error("...");
end
end
end
end
a >= b is equivalent to b <= a. Note that, in the absence of a "le" metamethod, Lua tries the "lt", assuming that a <= b is equivalent to not (b < a).
"index": 經過索引訪問 table[key]。
function gettable_event (table, key)
local h
if type(table) == "table" then
local v = rawget(table, key)
if v ~= nil then return v end
h = metatable(table).__index
if h == nil then return nil end
else
h = metatable(table).__index
if h == nil then
error("...");
end
end
if type(h) == "function" then
return h(table, key) -- call the handler
else return h[key] -- or repeat operation on it
end
"newindex": 給表的索引賦值 table[key] = value。
function settable_event (table, key, value)
local h
if type(table) == "table" then
local v = rawget(table, key)
if v ~= nil then rawset(table, key, value); return end
h = metatable(table).__newindex
if h == nil then rawset(table, key, value); return end
else
h = metatable(table).__newindex
if h == nil then
error("...");
end
end
if type(h) == "function" then
return h(table, key,value) -- call the handler
else h[key] = value -- or repeat operation on it
end
"call": 當Lua調用某個值時調用。
function function_event (func, ...)
if type(func) == "function" then
return func(unpack(arg)) -- primitive call
else
local h = metatable(func).__call
if h then
return h(func, unpack(arg))
else
error("...")
end
end
end
2.9 - 垃圾收集
Lua 會自動進行內存管理。這意味着你不須要擔憂新對象的內存分配問題,也不須要釋放不用的對象。Lua 經過不斷地運行 垃圾收集器 收集 dead objects (也就是那些Lua中沒法訪問的對象)來自動管理內存。Lua中全部的對象都是自動管理的目標:表,用戶數據,函數,線程,和字符串。Lua使用兩個數字控制垃圾收集循環。一個數字表示Lua使用的動態內存的字節數,另外一個是閥值。當內存字節數到達閥值時,Lua就運行垃圾收集器,來釋放死對象的空間。一旦字節計數器被調整,那麼閥值就會被設爲字節計數器新值的兩倍。
經過C API,你能夠查詢和更改閥值(見 3.7)。將閥值設爲零時會強制馬上進行垃圾收集,同時把他設爲足夠大就能夠中止垃圾收集。僅使用Lua代碼中的 gcinfo 和 collectgarbage 函數 (見 5.1)能夠得到必定程度上對垃圾收集循環的控制。
2.9.1 - 垃圾收集元方法 (Garbage-Collection Metamethods)
使用 C API,你能夠對用戶數據設置一個垃圾收集元方法(見 2.8)。這些元方法也稱爲 終結器(finalizers)。終結器容許你用外部的資源管理來調整Lua的垃圾收集(如關閉文件,網絡或數據庫鏈接,或者釋放你本身的內存。
用元表中包含 __gc 字段的自由用戶數據不會當即被垃圾收集器回收。而是,Lua把它們放在一個列表中。收集完畢以後,Lua會對這個列表中的用戶數據執行和如下函數相等的操做:
function gc_event (udata)
local h = metatable(udata).__gc
if h then
h(udata)
end
end
在每一個垃圾收集過程最後,調用用戶數據的終結器的順序,將按照他們在收集過程當中添加到列表中的相反順序進行。也就是,第一個被調用的終結器是和在程序中建立的最後一個用戶數據相關的那個終結器。
2.9.2 - 弱表
一個 弱表(weak table) 是一個包含的元素是 弱引用(weak references)的表。垃圾收集器會忽略弱引用。換句話說,若是指向一個對象的引用只有弱引用,那麼這個對象仍是要被垃圾收集器回收。
弱表能夠包含弱的鍵,弱的值,或者二者皆有。一個包含弱鍵的表容許它的鍵被回收,但值不能夠。一個同時包含弱鍵和弱值的表容許鍵和值的回收。不管哪一種狀況,只要鍵或者值中的一個被回收了,那麼這一對鍵值將會從表中刪除。這個表的弱屬性是由它的元表的 __mode 字段控制的。若是 __mode 字段是一個包含字符 `k´的字符串,那麼表中的鍵是弱鍵。若是 __mode 字段是一個包含字符 `v´ 的字符串,那麼表中的值是弱值。
在你將表用做元表以後,你不該該更改 __mode 字段的值。不然,這個元表控制的表的弱錶行爲將會不肯定。
2.10 - 同步程序
Lua支持同步程序,也稱爲 半同步程序(semi-coroutines) 或 協同多線程(collaborative multithreading)。Lua中的一個同步程序表明了一個獨立的執行線程。然而,不像在多線程系統中的線程那樣,一個同步程序只有在調用了一個yield(產生結果)函數才能掛起它的執行。
你能夠調用 coroutine.create 來建立一個同步程序。它惟一的一個參數是一個函數,表明同步程序的主函數。create 函數僅僅創建一個新的同步程序而後返回一個它的句柄 (一個線程 thread 類型的對象);它不會啓動該同步程序。
當你第一次調用 coroutine.resume,將 coroutine.create 返回的線程對象做爲第一個參數傳遞給它,而後同步程序就啓動了,從它的主函數的第一行開始。傳給 coroutine.resume 的額外的參數會做爲同步程序主函數的參數傳遞過去。在同步程序開始執行以後,它一直運行到它結束或產生結果。
一個同步程序經過兩種方式結束它的運行:正常狀況下,當它的主函數返回(顯式地或隱式的,在最後一個指令以後)時結束;異常地,若是有未保護的錯誤。第一各狀況下,coroutine.resume 返回 true,加上同步程序主函數返回的其它值。在有錯誤的狀況下,coroutine.resume 返回 false ,並附上錯誤信息。
一個同步程序經過調用 coroutine.yield 來產生結果。當一個同步程序產生結果,相應的 coroutine.resume 就馬上返回,即便操做發生在嵌套函數調用中(也就是,不在主函數中,而在被主函數直接或間接調用的函數中)。在這種狀況下, coroutine.resume 也返回 true,以及傳給 coroutine.yield。的全部參數。下次你繼續同一個同步程序時,它會從它原來yield的地方繼續執行,而 coroutine.yield 將返回給主程序傳給 coroutine.resume 的額外參數。
coroutine.wrap 函數建立一個和 coroutine.create 同樣的同步程序,但它不返回同步程序自己,而是返回一個繼續同步程序的函數(當調用的時候)。傳遞給這個函數的參數做爲繼續resume的額外參數。函數將返回resume返回的全部值,出除了第一個(布爾值的錯誤代碼)。不像 coroutine.resume,這個函數不捕獲錯誤;出現任何錯誤都傳回給調用者。
請考慮如下例子:
function foo1 (a)
print("foo", a)
return coroutine.yield(2a)
end
co = coroutine.create(function (a,b)
print("co-body", a, b)
local r = foo1(a+1)
print("co-body", r)
local r, s = coroutine.yield(a+b, a-b)
print("co-body", r, s)
return b, "end"
end)
a, b = coroutine.resume(co, 1, 10)
print("main", a, b)
a, b, c = coroutine.resume(co, "r")
print("main", a, b, c)
a, b, c = coroutine.resume(co, "x", "y")
print("main", a, b, c)
a, b = coroutine.resume(co, "x", "y")
print("main", a, b)
當你運行它的時候,它會產生如下輸出結果:
co-body 1 10
foo 2
main true 4
co-body r
main true 11 -9
co-body x y
main true 10 end
main false cannot resume dead coroutine 數據庫
3 - 應用程序接口
這一節描述Lua中的C API,這是對於宿主程序可用的C函數集合,用以和Lua通信。全部的API函數及其相關類型和常量都聲明在頭文件lua.h中。
即使每次我都使用「函數」這個詞,任何設施在API裏面均可能被一個宏所替代。全部這些宏(macro)都只使用一次它的參數(除了第一個參數、這個每次老是一個Lua狀態),因此不會產生隱藏的反作用。
3.1 - 狀態
Lua庫是可重入的(reentrant)的:它沒有全局變量。整個Lua解釋器的狀態(全局變量、棧、等等)儲存在一個動態分配的 lua_State 結構類型中。一個指向這個狀態的指針必須做爲庫中每個函數的第一個參數,除了 lua_open 這個函數。該函數從最開始建立一個Lua狀態。
在調用任何API函數以前,你必須經過調用 lua_open 建立一個狀態:
lua_State lua_open (void);
調用 lua_close 去釋放這個由 lua_open 建立的狀態:
void lua_close (lua_State L);
這個函數銷燬全部被給予Lua狀態的對象(調用相應的垃圾收集元方法)而且釋放那個狀態使用的全部動態內存。在個別的平臺上,你或許不須要調用這個函數,由於當宿主程序結束的時候會天然的釋放全部的資源。另外一方面,長時間運行的程序,像一些守護進程或者Web服務器,可能須要當即釋放那些不須要的狀態資源,以免佔用太多內存。
3.2 - 堆棧和索引
Lua使用一個來自於C語言的 虛擬棧(virtual stack) 傳遞值。棧裏面的每個元素都表明一個Lua值 (nil, number, string, etc.)。
只要Lua調用C語言函數,這個所調用的函數將獲得一個新的棧,這個棧將獨立於先前的棧以及那些仍然活躍的C函數的棧。這個棧最初包含了C函數的全部參數,而且這也會存放C函數的返回值(見 3.16)。
爲了方便起見,大多數查詢操做的API不須要遵照一個嚴格的棧定義(注:即不須要遵循FILO)。他們可使用 索引(index) 引用任何棧中元素:一個正數索引表明了棧中的絕對位置(從1開始);一個負數索引表明了從棧頂的偏移量。更特別的是,若是棧有 n 個元素,那麼索引 1 表明第一個元素(這就是說,這個元素首先入棧)而且索引 n 表明了最後一個元素;索引 -1 也表明了最後一個元素(也就是棧頂)而且索引 -n 表明了第一個元素。咱們說一個索引存在於 1 和棧頂之間是有效的,換句話說,若是 1 <= abs(index) <= top。
在任什麼時候間裏,你能夠調用 lua_gettop 獲得棧頂元素的索引:
int lua_gettop (lua_State L);
由於索引從 1 開始,lua_gettop 的結果等於棧中的元素數量(若是是0就意味着棧爲空)。
當你與Lua API交互的時候,你有責任控制堆棧以免溢出。。這個函數
int lua_checkstack (lua_State L, int extra);
使棧的大小增加爲 top + extra 個元素;若是沒法將棧增長到那個大小將返回false。這個函數從不對棧進行收縮;若是棧已經比新的大小更大,它將不產生任何做用那個。
只要Lua調用C 函數,它必須至少保證 LUA_MINSTACK 這個棧中的位置是可用的。LUA_MINSTACK 定義在 lua.h 中,它的值是 20,因此你不須要總擔憂棧空間除非你的代碼經過循環將元素壓入棧。
大多數插敘函數接受指向有效棧空間的索引,那就是說,索引達到棧空間的最大值是你須要使用 lua_checkstack。這樣的索引稱爲可接受索引(acceptable indices)。更正規的說法,咱們給出一個嚴格的定義以下:
(index < 0 && abs(index) <= top) || (index > 0 && index <= stackspace)
注意,0永遠不是一個可接受索引。
除非另外說明,任何函數接受有效索引能夠被稱爲是 僞索引(pseudo-indices),這些索引表明一些Lua值能夠被C 代碼訪問可是卻不存在於棧中。假索引一般用於訪問全局環境變量,註冊表,和一個C 函數的上值(見 3.17)。
3.3 - 堆棧操做
一下的API提供了基本的棧操做:
void lua_settop (lua_State L, int index);
void lua_pushvalue (lua_State L, int index);
void lua_remove (lua_State L, int index);
void lua_insert (lua_State L, int index);
void lua_replace (lua_State L, int index);
lua_settop 接受任何可接受的索引,或者0,而且將該索引設置爲棧頂。若是新的棧頂比舊的更大,那麼新元素被填上 nil 值。若是索引爲 0,那麼全部棧元素會被清除。在 lua.h 裏面定義了一個有用的宏
#define lua_pop(L,n) lua_settop(L, -(n)-1)
用以從棧中彈出 n 個元素。
lua_pushvalue 將一個索引指向的元素的拷貝壓入棧。 lua_remove 刪除指定位置的元素,將該元素上方的全部元素下移以填滿空缺。lua_insert 將棧頂元素移動到指定位置,將該位置以上的元素上移。lua_replace 將棧頂元素移動到指定位置而不移動其餘任何其餘元素(所以替代了給定位置的元素的值)。全部這些函數只接受有效的索引。(你不能使用僞索引調用 lua_remove 或 lua_insert,由於他們不表明棧中的位置。)
舉個例子,若是棧開始於 10 20 30 40 50(自底向上;`´ 標記了棧頂),那麼:
lua_pushvalue(L, 3) --> 10 20 30 40 50 30
lua_pushvalue(L, -1) --> 10 20 30 40 50 30 30
lua_remove(L, -3) --> 10 20 30 40 30 30
lua_remove(L, 6) --> 10 20 30 40 30
lua_insert(L, 1) --> 30 10 20 30 40
lua_insert(L, -1) --> 30 10 20 30 40 (no effect)
lua_replace(L, 2) --> 30 40 20 30
lua_settop(L, -3) --> 30 40
lua_settop(L, 6) --> 30 40 nil nil nil nil
3.4 - 堆棧查詢
下面的函數能夠用來檢測棧內元素的類型:
int lua_type (lua_State L, int index);
int lua_isnil (lua_State L, int index);
int lua_isboolean (lua_State L, int index);
int lua_isnumber (lua_State L, int index);
int lua_isstring (lua_State L, int index);
int lua_istable (lua_State L, int index);
int lua_isfunction (lua_State L, int index);
int lua_iscfunction (lua_State L, int index);
int lua_isuserdata (lua_State L, int index);
int lua_islightuserdata (lua_State L, int index);
這些函數只能使用可接受的索引。
lua_type 返回棧中元素值的類型,若是全部索引無效則返回 LUA_TNONE(就是說若是棧爲空)。這些lua_type 表明的返回值做爲常量定義在 lua.h 中:LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, LUA_TLIGHTUSERDATA。下面的函數將這些常量轉換成字符串:
const char lua_typename (lua_State L, int type);
lua_is 函數返回 1 當對象與所給類型兼容的時候,其餘狀況返回 0。 lua_isboolean 是一個例外:它只針對布爾值時纔會成功(不然將是無用的,由於任何值都是一個布爾值)。這些函數對於無效引用返回 0。 lua_isnumber 接受數字和用數字表示的字符串;lua_isstring 接受字符串和數字(見 2.2.1);lua_isfunction 接受Lua函數和C函數; lua_isuserdata 接受完整的和輕量的用戶數據。要區分C 函數和Lua 函數,你可使用 lua_iscfunction。要區分用戶數據,你可使用 lua_islightuserdata。要區分數字仍是用數字表示的字符串,你可使用 lua_type。
這些API還包含了用於比較棧中的兩個值的操做:
int lua_equal (lua_State L, int index1, int index2);
int lua_rawequal (lua_State L, int index1, int index2);
int lua_lessthan (lua_State L, int index1, int index2);
lua_equal 和 lua_lessthan 在比較他們的副本的時候是等效的(見 2.5.2)。 lua_rawequal 用於比較基本類型但不包括元方法。若是有任何形式的無效索引,這些函數都返回 0(false)。
3.5 - 堆棧取值
爲了將一個棧中的值轉變爲指定的C語言類型,你須要使用如下的轉換函數:
int lua_toboolean (lua_State L, int index);
lua_Number 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_CFunction lua_tocfunction (lua_State L, int index);
void lua_touserdata (lua_State L, int index);
lua_State lua_tothread (lua_State L, int index);
void lua_topointer (lua_State L, int index);
這些函數由任何可接受索引做爲參數進行調用。當遇到一個無效索引,函數表現爲就好像接受了一個錯誤類型的值。
lua_toboolean 將索引指向的Lua值轉換爲C語言類型的布爾值(0 或 1)。就像全部Lua中的測試同樣,任何不等於 false 或者 nil 的Lua值經過 lua_toboolean 都將返回 1;不然將返回 0。固然,若是是一個無效索引,也將返回 0。(若是你只想接受真實的布爾值,使用 lua_isboolean 去測試值的類型。)
lua_tonumber 將索引指向的Lua值轉換成一個數字(默認狀況下,lua_Number 是 double類型)。Lua值必須是一個數字或者可轉化爲數字的字符串(見 2.2.1);不然,lua_tonumber 返回 0。
lua_tostring 將索引指向的Lua值轉換成字符串(const char)。Lua值必須是一個字符串或者數字;不然,函數返回 NULL。若是值是一個數字,lua_tostring 會將棧中的真實值變成一個字符串類型。(當 lua_tostring 應用於鍵時這個改變將引發 lua_next 的混亂。)lua_tostring 在Lua 狀態內部返回一個字符串的指針。這個字符串老是以 0('/0')結尾,就像C 語言裏的同樣,可是也可能包含其餘 0 在其中。若是你不知道一個字符串中是否存在 0 ,你可使用 lua_strlen 獲得它的實際長度。由於Lua具備垃圾收集機制,因此不能保證 lua_tostring 返回的指針仍然有效,當相應的值從棧中刪除以後。若是你在當前函數返回以後還須要這個字符串,你須要複製它而且將它存入註冊表(見 3.18)。
lua_tocfunction 將棧中的值轉換爲C 函數。這個值必須是一個C 函數;不然,lua_tocfunction 返回 NULL。類型 lua_CFunction 在 3.16 中有詳細解釋。
lua_tothread 將棧中的值轉換爲Lua線程(被描繪成 lua_State )。這個值必須是一個線程;不然;lua_tothread 返回 NULL。
lua_topointer 將棧中的值轉換爲通用的C 語言指針(void )。這個值多是一個用戶數據、表、線程、或者函數;不然,lua_topointer 返回 NULL。Lua保證同種類型的不一樣對象將返回不一樣指針。沒有直接的方法將指針轉換回原來的值。這個函數一般用於調試。
lua_touserdata 在 3.8 中有詳細解釋。
3.6 - 將值壓入堆棧
如下的API函數將C 語言值壓入棧:
void lua_pushboolean (lua_State L, int b);
void lua_pushnumber (lua_State L, lua_Number n);
void lua_pushlstring (lua_State L, const char s, size_t len);
void lua_pushstring (lua_State L, const char s);
void lua_pushnil (lua_State L);
void lua_pushcfunction (lua_State L, lua_CFunction f);
void lua_pushlightuserdata (lua_State L, void p);
這些函數接受一個C 語言值,將其轉換成相應的Lua 值,而且將結果壓入棧。須要特別注意的是,lua_pushlstring 和 lua_pushstring 將對所給的字符串作一個內部拷貝。lua_pushstring 只能壓入合適的C 語言字符串(也就是說,字符串要以 '/0' 結尾,而且不能包含內嵌的 0);不然,你須要使用更通用的 lua_pushlstring 函數,它能夠接受一個指定的大小。
你能夠壓入「格式化的」字符串:
const char lua_pushfstring (lua_State L, const char fmt, ...);
const char lua_pushvfstring (lua_State L, const char fmt, va_list argp);
這些函數將格式化的字符串壓入棧而且返回這個字符串的指針。它們和 sprintf、vsprintf 相似,可是有一些重要的不一樣之處:
你不須要爲結果分配空間:結果是Lua字符串而且Lua會關心內存分配問題(和內存釋放問題,經過垃圾收集機制)。
轉換受到限制。這裏沒有標誌、寬度或精度。轉換操做的修飾符能夠是簡單的`%%´(在字符串中插入一個`%´),`%s´(插入一個沒有大小限制的以 0 結尾的字符串),`%f´(插入一個 lua_Number),`%d´(插入一個 int),`%c´(插入一個 int 做爲一個字符)。
這個函數
void lua_concat (lua_State L, int n);
鏈接棧頂的 n 個值,將它們彈出,而且將結果留在棧頂。若是 n 爲 1,結果是單個字符串(也就是說,函數什麼也不作);若是 n 是 0,結果是空字符串。鏈接的完成依據Lua的語義(見 2.5.4)。
3.7 - 控制垃圾收集
Lua使用兩個數字控制垃圾收集循環。一個數字表示Lua使用的動態內存的字節數,另外一個是閥值。(見 2.9)。一個數字表示Lua使用的動態內存的字節數,另外一個是閥值。當內存字節數到達閥值時,Lua就運行垃圾收集器,來釋放死對象的空間。一旦字節計數器被調整,那麼閥值就會被設爲字節計數器新值的兩倍。
你能夠經過如下的函數獲得這兩個量的當前值:
int lua_getgccount (lua_State L);
int lua_getgcthreshold (lua_State L);
它們的返回值的單位都是千字節(K bytes)。你能夠經過下面的函數改變閥值
void lua_setgcthreshold (lua_State L, int newthreshold);
而後,新的閥值得單位也是千字節。當你調用這個函數,Lua設置閥新值而且和字節計數器做比較。若是新的閥值小於字節計數器,Lua將馬上運行垃圾收集器。特別是 lua_setgcthreshold(L,0) 強迫進行垃圾收集。在這以後,一個新值根據先前的規則被設置。
3.8 - 用戶數據類型 (Userdata)
用戶數據表明了Lua中使用的C語言值。Lua支持兩種用戶數據:完整用戶數據(full userdata) 和 輕量用戶數據(light userdata)。
一個完整用戶數據表明了一塊內存。它是一個對象(像一個表):你必須建立它,它有本身的元表,當它被回收的時候你能夠檢測到。一個完整用戶數據只能與本身相等(基於原始的相等規則)。
一個輕量用戶數據表明一個指針。它是一個值(像一個數字):你並無建立它,它也沒有元表、,它不能被回收(由於它從未被建立)。輕量用戶數據相等的條件是指針指向的地址相同。
在Lua 代碼裏,沒辦法測試用戶數據類型是完整的仍是輕量的;二者都是 用戶數據類型。在C 代碼裏,若是是完整用戶數據,lua_type 返回 LUA_TUSERDATA,反之,返回 LUA_TLIGHTUSERDATA。
你能夠經過下面的函數建立完整用戶數據:
void lua_newuserdata (lua_State L, size_t size);
這個函數根據指定大小分配一個內存塊,將用戶數據的地址壓入棧而且返回這個地址。
要將輕量用戶數據壓入棧,你須要使用 lua_pushlightuserdata(見 3.6)。
lua_touserdata (見 3.5)用來取回用戶數據的值。當你用在完整用戶數據的時候,它返回這個塊的地址,當你用在輕量用戶數據的時候,它返回它的指針,當你用在非用數據的時候,返回 NULL。
當Lua回收一個完整用戶數據,它調用該用戶數據的 gc 元方法,而後釋放該用戶數據相應的內存。
3.9 - 元表 (Metatables)
下面的函數容許你操做對象的元表:
int lua_getmetatable (lua_State L, int index);
int lua_setmetatable (lua_State L, int index);
lua_getmetatable 將所給對象的元表壓入棧。若是索引無效,或這個對象不含有元表,該函數返回 0 而且不對棧進行任何操做。
lua_setmetatable 從棧中彈出一張表而且爲所給對象設置一個新的元表。當沒法給所給對象設置元表的時候該函數返回 0(也就是說,這個對象既不是一個用戶數據也不是一張表);儘管那樣,它仍從棧中彈出這張表。
3.10 - 加載Lua語句段
你能夠經過 lua_load 加載一個Lua塊:
typedef const char (lua_Chunkreader)
(lua_State L, void data, size_t size);
int lua_load (lua_State L, lua_Chunkreader reader, void data,
const char chunkname);
lua_load 的返回值是:
0 --- 沒有錯誤
LUA_ERRSYNTAX --- 預編譯時句法錯誤
LUA_ERRMEM --- 內存分配錯誤
若是沒有錯誤,lua_load 將編譯過的語句段做爲Lua 函數壓入棧頂。不然,它將壓入一個錯誤信息。
lua_load 自動檢測語句段的類型是文本仍是二進制數據,而且根據類型將其載入(見程序 luac)。
lua_load 使用一個用戶提供的 reader 函數讀取語句段的內容。當須要調用其它段時,lua_load 調用 reader,傳遞其 data 參數。必須返回指向語句段所在的新內存塊的指針,並將段大小設置爲 0。爲了標誌塊尾,reader 必須返回 NULL。reader 函數能夠返回任何大於零的值。
在當前的實現中,reader 函數不能調用任何Lua 函數;爲了保證這一點,它老是會獲得爲 NULL 的Lua狀態。
語句段名(chunkname) 用於錯誤信息和調試信息(見 4)。
參考輔助庫 (lauxlib.c) 瞭解如何使用 lua_load 以及如何使用現成的函數從文件和字符串中加載語句段。
3.11 - 表操做
經過調用如下函數能夠建立表:
void lua_newtable (lua_State L);
這個函數建立一張新的空表,並將其壓入棧。
要從棧中的表裏讀取值,使用:
void lua_gettable (lua_State L, int index);
index 表明表的位置。lua_gettable 從棧中彈出一個鍵,而且返回該鍵對應的值,表仍然留在堆棧中。在Lua中,這個函數可能觸發一個針對 index 事件的元方法(見 2.8)。想要在不調用任何元方法的狀況下獲得表主鍵所對應的真實值,使用這個原始(raw)版本:
void lua_rawget (lua_State L, int index);
要將一個值儲存到棧中的一張表中,你須要將鍵壓入棧,再將值壓入棧,調用:
void lua_settable (lua_State L, int index);
index 表明表的位置。lua_settable 從棧中彈出主鍵和值。表仍然留在棧中。在Lua中,這個操做可能觸發針對 settable 或者 newindex 事件的元方法。想要不受這些元方法的影響而且爲任意表設置值,使用這個原始(raw)版本:
void lua_rawset (lua_State L, int index);
你能夠經過這個函數遍歷一張表:
int lua_next (lua_State L, int index);
index 指向須要被遍歷的表。這個函數從堆棧中彈出一個鍵,從表中取一對鍵-值壓入棧(所給鍵的下一對)。若是沒有更多的元素,lua_next 返回 0(對棧不進行操做)。使用一個 nil 鍵標示遍歷的開始。
一個典型的遍歷操做看起來像這樣:
/ table is in the stack at index `t' /
lua_pushnil(L); / first key /
while (lua_next(L, t) != 0) {
/ `key' is at index -2 and `value' at index -1 /
printf("%s - %s/n",
lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1)));
lua_pop(L, 1); / removes `value'; keeps `key' for next iteration /
}
當遍歷一張表的時候,不要在鍵上直接調用 lua_tostring,除非你知道這個鍵確實是一個字符串。再次調用 lua_tostring 改變了所給索引指向的值;這使 lua_next 的調用發生混亂。
3.12 - 環境變量操做 (Manipulating Environments)
全部的全局變量保存在普通的Lua 表中,叫作環境變量。初始的環境變量被稱做全局環境變量。這張表老是在 LUA_GLOBALSINDEX 這個僞索引處。
要訪問或改變全局變量的值,你能夠對環境變量表使用常規的表操做。舉個例子,存取一個全局變量的值:
lua_pushstring(L, varname);
lua_gettable(L, LUA_GLOBALSINDEX);
你能夠改變一個Lua 線程的全局環境變量經過 lua_replace 函數。
如下的函數提供獲取、設置Lua函數的環境變量的功能:
void lua_getfenv (lua_State L, int index);
int lua_setfenv (lua_State L, int index);
lua_getfenv 將堆棧中 index 索引指向的函數的環境變量表壓入棧。若是函數是一個C 函數,lua_getfenv 將全局環境變量壓入棧。lua_setfenv 從棧中彈出一張表而且將其設置爲棧中 index 索引處的函數的新環境變量。若是給定索引處的對象不是一個Lua 函數,lua_setfenv 返回 0。
3.13 - 將表做爲數組使用 Using Tables as Arrays
有一些 API 可以幫助咱們將Lua 表做爲數組使用,也就是說,表只由數字做爲索引:
void lua_rawgeti (lua_State L, int index, int n);
void lua_rawseti (lua_State L, int index, int n);
lua_rawgeti 將表中的第 n 個元素放入堆棧中的指定位置 index。lua_rawseti 將堆棧中指定位置 index 處的表中的第 n 個元素的值設定爲棧頂的值,並將原來的值從棧中刪除。
3.14 - 調用函數
定義在Lua 中的函數和C語言函數通過註冊就能夠被宿主程序調用。這些調用必須遵循如下協議:首先,被調用的函數被壓入棧;而後,函數的參數必須順序(direct order)輸入,也就是說,第一個參數須要被第一個輸入。最後,函數經過下面的方法調用:
void lua_call (lua_State L, int nargs, int nresults);
nargs 是你壓入棧的參數的數量。全部參數和函數值從堆棧中彈出,而且函數結果被壓入棧。返回值的數量被調整爲 nresults,除非 nresults 是 LUA_MULTRET。在那種狀況下,全部函數結果都被壓入棧。Lua 會檢測返回值是否適合棧空間。函數返回值按順序被壓入棧(第一個返回值首先入棧),因此調用結束後最後一個返回值在棧頂。
下面的例子展現宿主程序如何能夠和這個Lua 代碼等效:
a = f("how", t.x, 14)
這裏是C 語言裏的作法:
lua_pushstring(L, "t");
lua_gettable(L, LUA_GLOBALSINDEX); / global `t' (for later use) /
lua_pushstring(L, "a"); / var name /
lua_pushstring(L, "f"); / function name /
lua_gettable(L, LUA_GLOBALSINDEX); / function to be called /
lua_pushstring(L, "how"); / 1st argument /
lua_pushstring(L, "x"); / push the string "x" /
lua_gettable(L, -5); / push result of t.x (2nd arg) /
lua_pushnumber(L, 14); / 3rd argument /
lua_call(L, 3, 1); / call function with 3 arguments and 1 result /
lua_settable(L, LUA_GLOBALSINDEX); / set global variable `a' /
lua_pop(L, 1); / remove `t' from the stack /
注意上面的代碼是「平衡的」:在它結束時,堆棧返回原來的配置。這個被認爲是良好的編程實踐。
(爲了展現細節,咱們只用Lua 提供的原始 API 完成這個例子。一般程序員定義並使用幾個宏和輔助庫函數在Lua 中提供高級存取功能。請參考例子中標準庫函數的源代碼。)
3.15 - 受保護調用 Protected Calls
當你經過 lua_call 調用一個函數,所調用函數內部產生的錯誤將向上傳遞(經過一個 longjmp)。若是你須要處理錯誤,你應該使用 lua_pcall:
int lua_pcall (lua_State L, int nargs, int nresults, int errfunc);
nargs 和 nresults 在 lua_call 中有相同的意義。若是調用過程當中沒有錯誤,lua_pcall 的行爲很是像 lua_call 。然而,若是有錯誤,lua_call 會捕獲它,將一個單一值(錯誤信息)壓入棧,而且返回錯誤代碼。像 lua_call ,lua_pcall 老是從棧中刪除函數和它的參數。
若是 errfunc 是 0,所返回的錯誤信息就是原始的錯誤信息。另外,errfunc 給出一個指向錯誤處理函數(error handler function)的棧索引。(在當前的實現中,索引不能爲僞索引。)假設運行時錯誤,函數將和錯誤信息一塊兒被調用,而且他的返回值將是 lua_pcall 返回的信息。
錯誤處理函數被用來爲錯誤信息增長更多的調試信息,例如棧的記錄。這樣的信息在 lua_pcall 調用返回後將不能被收集,所以棧已經被解開了。
若是 lua_pcall 函數調用成功返回 0,不然返回如下的一個錯誤代碼(定義在 lua.h):
LUA_ERRRUN --- 運行時錯誤
LUA_ERRMEM --- 內存分配錯誤。這樣的錯誤下,Lua 不調用錯誤處理函數
LUA_ERRERR --- 運行錯誤處理函數時發生的錯誤
3.16 - 定義C 函數
Lua能夠經過C 語言寫的函數進行擴展,這些函數必須是 lua_CFunction 類型的,做爲如下定義:
typedef int (lua_CFunction) (lua_State L);
一個C 函數接收一個Lua 狀態而且返回一個整數,數值須要返回給Lua。
爲了正確的和Lua 通信,C 函數必須遵循如下協議,它定義了參數和返回值傳遞的方法:一個C 函數在它的堆棧中從Lua獲取順序(第一個參數首先入棧)參數。因此,當函數開始時,第一個參數在索引位置 1。爲了將返回值傳遞給Lua,一個C 函數將它們順序壓入棧,而且返回它們的數量。任何在堆棧中位於返回值如下的值都將被Lua 適當的解除。就像Lua 函數同樣,一個C 函數被Lua 調用也能夠返回不少結果。
做爲一個例子,下面的函數接收一個任意數量的數字參數而且返回它們的平均值和總合:
static int foo (lua_State L) {
int n = lua_gettop(L); / number of arguments /
lua_Number sum = 0;
int i;
for (i = 1; i <= n; i++) {
if (!lua_isnumber(L, i)) {
lua_pushstring(L, "incorrect argument to function `average'");
lua_error(L);
}
sum += lua_tonumber(L, i);
}
lua_pushnumber(L, sum/n); / first result /
lua_pushnumber(L, sum); / second result /
return 2; / number of results /
}
下面是一些便利的宏用來在Lua中註冊一個C 函數:
#define lua_register(L,n,f) /
(lua_pushstring(L, n), /
lua_pushcfunction(L, f), /
lua_settable(L, LUA_GLOBALSINDEX))
/ lua_State L; /
/ const char n; /
/ lua_CFunction f; /
它接收Lua 中的函數名和一個指向函數的指針。這樣,上面的C 函數foo能夠在Lua中被註冊爲 average 並被調用。
lua_register(L, "average", foo);
3.17 - 定義C 函數關閉 Defining C Closures
當一個C 函數被建立後,它能夠與一些值關聯,這樣建立了一個 C 閉包(C closure);這些值能夠被隨時被函數訪問。爲了使值和C 函數關聯,首先這些值要被壓入棧(有多個值時,第一個值先入),而後這個函數
void lua_pushcclosure (lua_State L, lua_CFunction fn, int n);
被用來將C 函數壓入棧,經過參數 n 告知應該有多少個值和該函數關聯(lua_pushcclosure 將這些值從堆棧中彈出);事實上,這個宏 lua_pushcfunction 被定義做爲 lua_pushcfunction 將 n 設置爲 0。
而後,不管什麼時候C 函數被調用,那些值被定爲於指定的僞索引處。那些僞索引有一個宏 lua_upvalueindex 產生。第一個和函數關聯的值在 lua_upvalueindex(1) 處,其餘的以此類推。當 n 比當前函數的上值大時,lua_upvalueindex(n) 會產生一個可接受的索引(可是無效)。
C語言函數和關閉的例子,請參考Lua官方發行版中的標準庫(src/lib/.c)。
3.18 - 註冊表 Registry
Lua提供了一個註冊表,一張能夠被全部C 代碼用來儲存任何須要儲存的Lua值的預約義表,特別是若是C 代碼須要維護C 函數之外存活的Lua值。這張表老是位於 LUA_REGISTRYINDEX 這個爲索引處。任何C 語言庫能夠將數據儲存在這張表中,只要它選擇的鍵和其餘庫不一樣。典型的作法是你應該使用字符串做爲主鍵包含你的庫名或者在你的代碼中使用一個包含C 對象地址的輕量用戶數據。
在註冊表中的整數鍵被引用機制所使用,由輔助庫實現,所以不該該被用做其它用途。
3.19 - C 中的錯誤處理 Error Handling in C
總的來講,Lua使用C longjmp 機制來處理錯誤。當Lua面對任何錯誤(例如內存分配錯誤,類型錯誤,句法錯誤)它 升起(raises) 一個錯誤,也就是說,它作了一個長跳躍。一個受保護的環境使用 setjmp 設置一個恢復點;任何錯誤跳至最近最活躍的恢復點。
若是錯誤發生在任何受保護的環境,Lua調用 panic 函數 而且隨後調用exit(EXIT_FAILURE)。你能夠將panic 函數變爲如下內容。
lua_CFunction lua_atpanic (lua_State L, lua_CFunction panicf);
你的新panic 函數能夠避免程序由於沒有返回(例如經過一個長跳躍)而退出。不然,相應的Lua 狀態將不一致;惟一安全的操做就是關閉它。
幾乎全部的API 函數均可能引發錯誤,例如致使一個內存分配錯誤。:lua_open, lua_close, lua_load 和 lua_pcall 這些的函數運行在保護模式下(也就是說,它們建立了一個受保護的環境並在其中運行),因此它們從不會引發錯誤。
有另一個函數將所給的C 函數運行在保護模式下:
int lua_cpcall (lua_State L, lua_CFunction func, void ud);
lua_cpcall 在保護模式下調用 func。func 由一個包含 ud 的輕量用戶數據開始。在錯誤問題上,lua_cpcall 像 lua_pcall 同樣返回相同的錯誤代碼(見 3.15),加上在棧頂的一個錯誤對象;不然,返回 0,而且不對堆棧進行任何操做。任何由 func 返回的值都被丟棄。
C 代碼能夠經過調用下面的函數產生一個Lua錯誤:
void lua_error (lua_State L);
錯誤信息(實際上能夠是任何類型的對象)必須在棧頂。這個函數進行一個長跳躍,所以歷來不會返回。
3.20 - 線程
Lua 提供了操做線程的部分支持。若是你有多線程操做的C 語言庫,那麼Lua可以與其協做而且在Lua中實現相同的機制。一樣,Lua在線程之上實現本身的協同程序系統。如下函數用來在Lua中建立一個線程:
lua_State lua_newthread (lua_State L);
這個函數將線程壓入棧而且返回表明新線程的 lua_State 指針。這個返回的新狀態與全部全局對象(例如表)共享初始狀態,可是有一個獨立的運行時堆棧。
每一個線程都有本身獨立的全局環境表。當你建立一個線程,這張表就和所給狀態同樣,可是你能夠獨自更改它們。
沒有明確的函數能夠關閉或者銷燬一個線程。線程和其餘Lua對象同樣受垃圾收集程序的支配:
要像協同程序同樣操做線程,Lua提供瞭如下函數:
int lua_resume (lua_State L, int narg);
int lua_yield (lua_State L, int nresults);
你須要建立一個線程以便啓動協同程序;而後你將函數體和事件參數壓入堆棧;而後調用 lua_resume,narg 的值表明參數的數量。當同步程序暫停或者結束執行,函數將返回。當它返回後,棧中包含的全部值傳遞給 lua_yield,或者有主體函數返回。若是同步程序運行無誤,lua_resume 返回 0,不然返回一個錯誤代碼(見 3.15)。對於錯誤,堆棧只包含錯誤信息。要重起同步程序,將做爲結果傳遞給 yield 的值壓入堆棧,而且調用 lua_resume。
lua_yield 函數只能像C 函數的返回表達式同樣被調用,就像下面所展現的:
return lua_yield (L, nresults);
若是C 函數像這樣調用 lua_yield,正在運行的同步程序暫停它的執行,而且調用 lua_resume 開始讓這個協同程序返回。nresults 這個參數表明了在堆棧中做爲結果傳遞給 lua_resume 的值的數量。
要在不一樣線程中交換值,你可使用 lua_xmove:
void lua_xmove (lua_State from, lua_State to, int n);
它從堆棧 from 中彈出 n 個值,並將其壓入堆棧 to。
4 - 調試接口 The Debug Interface
Lua 沒有內置的調試設施。它使用一種特殊的接口,這種接口依賴函數和 鉤子(hooks)。該接口容許構造不一樣種類的調試器,分析器以及其餘工具用以從解釋器獲得所需的信息。
4.1 - 堆棧及函數信息 Stack and Function Information
獲得解釋程序運行時堆棧信息的主要函數是:
int lua_getstack (lua_State L, int level, lua_Debug ar);
這個函數用一個指定等級的函數的 activation record 的標示符填充一個 lua_Debug 結構,等級 0 是當前運行函數,然而等級 n+1 是在等級 n 上調用的函數。當沒有錯誤發生時,lua_getstack 返回 1;當在比棧更深的等級上調用的時候,它返回 0;
lua_Debug 結構被用來攜帶一個處於活動狀態的函數的各類信息:
typedef struct lua_Debug {
int event;
const char name; / (n) /
const char namewhat; / (n) `global', `local', `field', `method' /
const char what; / (S) `Lua' function, `C' function, Lua `main' /
const char source; / (S) /
int currentline; / (l) /
int nups; / (u) number of upvalues /
int linedefined; / (S) /
char short_src[LUA_IDSIZE]; / (S) /
/ private part /
...
} lua_Debug;
lua_getstack 只填充結構的私有部分以備以後使用。要填充 lua_Debug 其餘有用信息,調用
int lua_getinfo (lua_State L, const char what, lua_Debug ar);
這個函數發生錯誤是返回 0 (舉個例子,一個無效的 what 選項)。what 字符串中的每一個字符選擇填充一些 ar 結構的字段,把上面在 lua_Debug 定義中用圓括號括起來的字母做爲指示: `S´ 填充在 source, linedefined 和 what 字段中;`l´ 填充在 currentline 字段中,等等...。並且,`f´ 將正在運行在所給等級上的函數壓入堆棧。
想要從一個不處於活動狀態的函數那獲得信息(就是不在棧上的函數),你須要將其壓入棧而且用 >´ 做爲 what 字符串的開始。舉個例子,要知道函數 f 定義在第幾行,你須要這樣寫
lua_Debug ar;
lua_pushstring(L, "f");
lua_gettable(L, LUA_GLOBALSINDEX); / get global `f' /
lua_getinfo(L, ">S", &ar);
printf("%d/n", ar.linedefined);
lua_Debug 的字段有以下的含義:
source 若是函數在一個字符串中定義,那麼 source 就是那個字符串。若是函數定義在一個文件中,source 開始於一個 `@´ 後面跟隨文件名。
short_src 一個可打印版的 source,用於錯誤信息。
linedefined 函數定義起始的行號。
what 若是這是一個Lua函數,顯示 "Lua" 字符串, "C" 爲C 函數,"main" 若是這是一個語句段的main部分,"tail" 若是這是一個作了尾部調用的函數。在後面的狀況裏,Lua 沒有其餘關於這個函部的信息。
currentline 表明當前函數執行到的行數。若是沒有行信息可用,currentline 被設置爲 -1。
name 一個所給函數合理的函數名。由於函數在Lua中屬於第一類值,它們沒有固定的名字:一些函數多是多個全局變量的值,其餘的可能只儲存在一個表字段裏。lua_getinfo 函數檢測函數如何被調用或者是否爲一個全局變量的值以尋找一個合適的名字。若是找不到合適的名字,name 被設置爲 NULL。
namewhat name 字段的解釋。根據函數如何被調用,namewhat 的值能夠是 "global", "local", "method", "field" 或者 "" (空字符串)。(當沒有其餘可選項的時候Lua使用空字符串代替)
nups 函數上值的數量。
4.2 - 操做局部變量和上值 Manipulating Local Variables and Upvalues
爲了更多的操做局部變量和上值,調試接口使用索引:第一個參數或者局部變量索引爲 1,以此類推,直到最後一個活動的局部變量。整個函數中的活動的上值沒有特定的順序。
下面的函數容許操做一個所給激活記錄的局部變量:
const char lua_getlocal (lua_State L, const lua_Debug ar, int n);
const char lua_setlocal (lua_State L, const lua_Debug ar, int n);
參數 ar 必須是一個被前一個 lua_getstack 調用填充的有效的激活記錄或者做爲一個鉤子函數的參數(見 4.3)。lua_getlocal 得到一個局部變量的索引 n,將變量的值壓入棧,而且返回變量名。lua_setlocal 從棧頂分配一個值給變量而且返回變量名。當索引超過活動的局部變量的數量時,兩個函數都返回 NULL。
如下的函部能夠操做所給函數的上值(不像局部變量,函數的上值即便在函數不處於活動狀態的時候均可以被訪問):
const char lua_getupvalue (lua_State L, int funcindex, int n);
const char lua_setupvalue (lua_State L, int funcindex, int n);
這些函數能夠做爲Lua 函數使用也能夠做爲C 函數使用。(做爲Lua 函數,上值是函數外部使用的局部變量,所以它被包含在函數閉包中。)funcindex 指向棧中的一個函數。lua_getupvalue 獲得一個上值的索引 n,將上值的值壓入棧,並返回其變量名。lua_setupvalue 從棧頂分配一個值給上值並返回變量名。當索引大於上值數量時,兩個函數都返回 NULL。對於C 函數來講,這些函數使用空字符串做爲全部上值的變量名。
做爲一個例子,下面的函數列舉了所給等級的棧中的函數的全部局部變量名和上值變量名:
int listvars (lua_State L, int level) {
lua_Debug ar;
int i;
const char name;
if (lua_getstack(L, level, &ar) == 0)
return 0; / failure: no such level in the stack /
i = 1;
while ((name = lua_getlocal(L, &ar, i++)) != NULL) {
printf("local %d %s/n", i-1, name);
lua_pop(L, 1); / remove variable value /
}
lua_getinfo(L, "f", &ar); / retrieves function /
i = 1;
while ((name = lua_getupvalue(L, -1, i++)) != NULL) {
printf("upvalue %d %s/n", i-1, name);
lua_pop(L, 1); / remove upvalue value /
}
return 1;
}
4.3 - 鉤子 Hooks
Lua offers a mechanism of hooks, which are user-defined C functions that are called during the program execution. A hook may be called in four different events: a call event, when Lua calls a function; a return event, when Lua returns from a function; a line event, when Lua starts executing a new line of code; and a count event, which happens every "count" instructions. Lua identifies these events with the following constants: LUA_HOOKCALL, LUA_HOOKRET (or LUA_HOOKTAILRET, see below), LUA_HOOKLINE, and LUA_HOOKCOUNT.
A hook has type lua_Hook, defined as follows:
typedef void (lua_Hook) (lua_State L, lua_Debug ar);
You can set the hook with the following function:
int lua_sethook (lua_State L, lua_Hook func, int mask, int count);
func is the hook. mask specifies on which events the hook will be called: It is formed by a disjunction of the constants LUA_MASKCALL, LUA_MASKRET, LUA_MASKLINE, and LUA_MASKCOUNT. The count argument is only meaningful when the mask includes LUA_MASKCOUNT. For each event, the hook is called as explained below:
The call hook is called when the interpreter calls a function. The hook is called just after Lua enters the new function.
The return hook is called when the interpreter returns from a function. The hook is called just before Lua leaves the function.
The line hook is called when the interpreter is about to start the execution of a new line of code, or when it jumps back in the code (even to the same line). (This event only happens while Lua is executing a Lua function.)
The count hook is called after the interpreter executes every count instructions. (This event only happens while Lua is executing a Lua function.)
A hook is disabled by setting mask to zero.
You can get the current hook, the current mask, and the current count with the following functions:
lua_Hook lua_gethook (lua_State L);
int lua_gethookmask (lua_State L);
int lua_gethookcount (lua_State L);
Whenever a hook is called, its ar argument has its field event set to the specific event that triggered the hook. Moreover, for line events, the field currentline is also set. To get the value of any other field in ar, the hook must call lua_getinfo. For return events, event may be LUA_HOOKRET, the normal value, or LUA_HOOKTAILRET. In the latter case, Lua is simulating a return from a function that did a tail call; in this case, it is useless to call lua_getinfo.
While Lua is running a hook, it disables other calls to hooks. Therefore, if a hook calls back Lua to execute a function or a chunk, that execution occurs without any calls to hooks.
5 - 標準庫
The standard libraries provide useful functions that are implemented directly through the C API. Some of these functions provide essential services to the language (e.g., type and getmetatable); others provide access to "outside" services (e.g., I/O); and others could be implemented in Lua itself, but are quite useful or have critical performance to deserve an implementation in C (e.g., sort).
All libraries are implemented through the official C API and are provided as separate C modules. Currently, Lua has the following standard libraries:
基本庫 basic library;
字符串操做 string manipulation;
表操做 table manipulation;
數學函數 (sin, log 等等)mathematical functions (sin, log, etc.);
輸入輸出 input and output;
操做系統機制 operating system facilities;
調試機制 debug facilities.
Except for the basic library, each library provides all its functions as fields of a global table or as methods of its objects.
To have access to these libraries, the C host program must first call the functions luaopen_base (for the basic library), luaopen_string (for the string library), luaopen_table (for the table library), luaopen_math (for the mathematical library), luaopen_io (for the I/O and the Operating System libraries), and luaopen_debug (for the debug library). These functions are declared in lualib.h. express
5.1 - 基本函數 Basic Functions
The basic library provides some core functions to Lua. If you do not include this library in your application, you should check carefully whether you need to provide some alternative implementation for some of its facilities.
assert (v [, message])
Issues an error when the value of its argument v is nil or false; otherwise, returns this value. message is an error message; when absent, it defaults to "assertion failed!"
collectgarbage ([limit])
Sets the garbage-collection threshold to the given limit (in Kbytes) and checks it against the byte counter. If the new threshold is smaller than the byte counter, then Lua immediately runs the garbage collector (see 2.9). If limit is absent, it defaults to zero (thus forcing a garbage-collection cycle).
dofile (filename)
Opens the named file and executes its contents as a Lua chunk. When called without arguments, dofile executes the contents of the standard input (stdin). Returns any value returned by the chunk. In case of errors, dofile propagates the error to its caller (that is, it does not run in protected mode).
error (message [, level])
Terminates the last protected function called and returns message as the error message. Function error never returns.
The level argument specifies where the error message points the error. With level 1 (the default), the error position is where the error function was called. Level 2 points the error to where the function that called error was called; and so on.
_G
A global variable (not a function) that holds the global environment (that is, _G._G = _G). Lua itself does not use this variable; changing its value does not affect any environment. (Use setfenv to change environments.)
getfenv (f)
Returns the current environment in use by the function. f can be a Lua function or a number, which specifies the function at that stack level: Level 1 is the function calling getfenv. If the given function is not a Lua function, or if f is 0, getfenv returns the global environment. The default for f is 1.
If the environment has a "__fenv" field, returns the associated value, instead of the environment.
getmetatable (object)
If the object does not have a metatable, returns nil. Otherwise, if the object's metatable has a "__metatable" field, returns the associated value. Otherwise, returns the metatable of the given object.
gcinfo ()
Returns two results: the number of Kbytes of dynamic memory that Lua is using and the current garbage collector threshold (also in Kbytes).
ipairs (t)
Returns an iterator function, the table t, and 0, so that the construction
for i,v in ipairs(t) do ... end
will iterate over the pairs (1,t[1]), (2,t[2]), ..., up to the first integer key with a nil value in the table.
loadfile (filename)
Loads a file as a Lua chunk (without running it). If there are no errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment.
loadlib (libname, funcname)
Links the program with the dynamic C library libname. Inside this library, looks for a function funcname and returns this function as a C function.
libname must be the complete file name of the C library, including any eventual path and extension.
This function is not supported by ANSI C. As such, it is only available on some platforms (Windows, Linux, Solaris, BSD, plus other Unix systems that support the dlfcn standard).
loadstring (string [, chunkname])
Loads a string as a Lua chunk (without running it). If there are no errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment.
The optional parameter chunkname is the name to be used in error messages and debug information.
To load and run a given string, use the idiom
assert(loadstring(s))()
next (table [, index])
Allows a program to traverse all fields of a table. Its first argument is a table and its second argument is an index in this table. next returns the next index of the table and the value associated with the index. When called with nil as its second argument, next returns the first index of the table and its associated value. When called with the last index, or with nil in an empty table, next returns nil. If the second argument is absent, then it is interpreted as nil.
Lua has no declaration of fields; There is no difference between a field not present in a table or a field with value nil. Therefore, next only considers fields with non-nil values. The order in which the indices are enumerated is not specified, even for numeric indices. (To traverse a table in numeric order, use a numerical for or the ipairs function.)
The behavior of next is undefined if, during the traversal, you assign any value to a non-existent field in the table.
pairs (t)
Returns the next function and the table t (plus a nil), so that the construction
for k,v in pairs(t) do ... end
will iterate over all key-value pairs of table t.
pcall (f, arg1, arg2, ...)
Calls function f with the given arguments in protected mode. That means that any error inside f is not propagated; instead, pcall catches the error and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, pcall also returns all results from the call, after this first result. In case of any error, pcall returns false plus the error message.
print (e1, e2, ...)
Receives any number of arguments, and prints their values in stdout, using the tostring function to convert them to strings. This function is not intended for formatted output, but only as a quick way to show a value, typically for debugging. For formatted output, use format (see 5.3).
rawequal (v1, v2)
Checks whether v1 is equal to v2, without invoking any metamethod. Returns a boolean.
rawget (table, index)
Gets the real value of table[index], without invoking any metamethod. table must be a table; index is any value different from nil.
rawset (table, index, value)
Sets the real value of table[index] to value, without invoking any metamethod. table must be a table, index is any value different from nil, and value is any Lua value.
require (packagename)
Loads the given package. The function starts by looking into the table _LOADED to determine whether packagename is already loaded. If it is, then require returns the value that the package returned when it was first loaded. Otherwise, it searches a path looking for a file to load.
If the global variable LUA_PATH is a string, this string is the path. Otherwise, require tries the environment variable LUA_PATH. As a last resort, it uses the predefined path "?;?.lua".
The path is a sequence of templates separated by semicolons. For each template, require will change each interrogation mark in the template to packagename, and then will try to load the resulting file name. So, for instance, if the path is
"./?.lua;./?.lc;/usr/local/?/?.lua;/lasttry"
a require "mod" will try to load the files ./mod.lua, ./mod.lc, /usr/local/mod/mod.lua, and /lasttry, in that order.
The function stops the search as soon as it can load a file, and then it runs the file. After that, it associates, in table _LOADED, the package name with the value that the package returned, and returns that value. If the package returns nil (or no value), require converts this value to true. If the package returns false, require also returns false. However, as the mark in table _LOADED is false, any new attempt to reload the file will happen as if the package was not loaded (that is, the package will be loaded again).
If there is any error loading or running the file, or if it cannot find any file in the path, then require signals an error.
While running a file, require defines the global variable _REQUIREDNAME with the package name. The package being loaded always runs within the global environment.
setfenv (f, table)
Sets the current environment to be used by the given function. f can be a Lua function or a number, which specifies the function at that stack level: Level 1 is the function calling setfenv.
As a special case, when f is 0 setfenv changes the global environment of the running thread.
If the original environment has a "__fenv" field, setfenv raises an error.
setmetatable (table, metatable)
Sets the metatable for the given table. (You cannot change the metatable of a userdata from Lua.) If metatable is nil, removes the metatable of the given table. If the original metatable has a "__metatable" field, raises an error.
tonumber (e [, base])
Tries to convert its argument to a number. If the argument is already a number or a string convertible to a number, then tonumber returns that number; otherwise, it returns nil.
An optional argument specifies the base to interpret the numeral. The base may be any integer between 2 and 36, inclusive. In bases above 10, the letter `A´ (in either upper or lower case) represents 10, `B´ represents 11, and so forth, with `Z´ representing 35. In base 10 (the default), the number may have a decimal part, as well as an optional exponent part (see 2.2.1). In other bases, only unsigned integers are accepted.
tostring (e)
Receives an argument of any type and converts it to a string in a reasonable format. For complete control of how numbers are converted, use format (see 5.3).
If the metatable of e has a "__tostring" field, tostring calls the corresponding value with e as argument, and uses the result of the call as its result.
type (v)
Returns the type of its only argument, coded as a string. The possible results of this function are "nil" (a string, not the value nil), "number", "string", "boolean, "table", "function", "thread", and "userdata".
unpack (list)
Returns all elements from the given list. This function is equivalent to
return list[1], list[2], ..., list[n]
except that the above code can be written only for a fixed n. The number n is the size of the list, as defined for the table.getn function.
_VERSION
A global variable (not a function) that holds a string containing the current interpreter version. The current content of this string is "Lua 5.0".
xpcall (f, err)
This function is similar to pcall, except that you can set a new error handler.
xpcall calls function f in protected mode, using err as the error handler. Any error inside f is not propagated; instead, xpcall catches the error, calls the err function with the original error object, and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, xpcall also returns all results from the call, after this first result. In case of any error, xpcall returns false plus the result from err. 編程
NGACN數組
6 - Lua 獨立程序 Lua Stand-alone
儘管Lua被設計爲一種內嵌於C 語言宿主程序中的擴展語言,它仍是常常被用做一個獨立程序語言。一個Lua的解釋程序將Lua做爲一個獨立的語言,咱們稱之爲簡化的 lua,它提供了標準的發行版本。獨立的解釋器包含了全部標準庫加上反射性的調試接口。它的使用方式以下:
lua [options] [script [args]]
options 能夠是如下內容:
- 將 標準輸入(stdin) 看成文件執行;
-e stat 執行字符串 stat;
-l file 「須要」file 文件;
-i 在運行腳本後進入交互模式;
-v 打印版本信息;
-- 中止處理選項。
在中止處理選項後,lua 運行所給的腳本,傳遞所給參數 args。當無參數調用時,lua 就像 stdin 是程序的終結時 lua -v -i 所表現的同樣,並且還與 lua- 同樣。
Before running any argument, the interpreter checks for an environment variable LUA_INIT. If its format is @filename, then lua executes the file. Otherwise, lua executes the string itself.
All options are handled in order, except -i. For instance, an invocation like
$ lua -e'a=1' -e 'print(a)' script.lua
will first set a to 1, then print a, and finally run the file script.lua. (Here, $ is the shell prompt. Your prompt may be different.)
Before starting to run the script, lua collects all arguments in the command line in a global table called arg. The script name is stored in index 0, the first argument after the script name goes to index 1, and so on. The field n gets the number of arguments after the script name. Any arguments before the script name (that is, the interpreter name plus the options) go to negative indices. For instance, in the call
$ lua -la.lua b.lua t1 t2
the interpreter first runs the file a.lua, then creates a table
arg = { [-2] = "lua", [-1] = "-la.lua", [0] = "b.lua",
[1] = "t1", [2] = "t2"; n = 2 }
and finally runs the file b.lua.
在交互模式中,若是你寫入了一個不完整的語句,解釋器將等待你的完成。
If the global variable _PROMPT is defined as a string, then its value is used as the prompt. Therefore, the prompt can be changed directly on the command line:
$ lua -e"_PROMPT='myprompt> '" -i
(the outer pair of quotes is for the shell, the inner is for Lua), or in any Lua programs by assigning to _PROMPT. Note the use of -i to enter interactive mode; otherwise, the program would end just after the assignment to _PROMPT.
在Unix系統中,Lua腳本能夠用 chmod +x 將其變成可執行程序,而且經過 #! 形式,例如
#!/usr/local/bin/lua
(固然,Lua解釋器的位置可能有所不一樣,若是 lua 在你的 PATH 中,那麼
#!/usr/bin/env lua
就是一個更通用的解決方案。)
致謝
The Lua team is grateful to Tecgraf for its continued support to Lua. We thank everyone at Tecgraf, specially the head of the group, Marcelo Gattass. At the risk of omitting several names, we also thank the following individuals for supporting, contributing to, and spreading the word about Lua: Alan Watson. André Clinio, André Costa, Antonio Scuri, Asko Kauppi, Bret Mogilefsky, Cameron Laird, Carlos Cassino, Carlos Henrique Levy, Claudio Terra, David Jeske, Ed Ferguson, Edgar Toernig, Erik Hougaard, Jim Mathies, John Belmonte, John Passaniti, John Roll, Jon Erickson, Jon Kleiser, Mark Ian Barlow, Nick Trout, Noemi Rodriguez, Norman Ramsey, Philippe Lhoste, Renata Ratton, Renato Borges, Renato Cerqueira, Reuben Thomas, Stephan Herrmann, Steve Dekorte, Thatcher Ulrich, Tomás Gorham, Vincent Penquerc'h. Thank you!
--------------------------------------------------------------------------------
與之前版本的不兼容性 Incompatibilities with Previous Versions
Lua 5.0 是一個主版本,全部與 Lua 4.0 有一些地方不兼容。
與 v4.0 的不兼容性 Incompatibilities with version 4.0
語言上的變更
整個標籤方法模式被元表所替代。The whole tag-method scheme was replaced by metatables.
Function calls written between parentheses result in exactly one value.
A function call as the last expression in a list constructor (like {a,b,f()}) has all its return values inserted in the list.
The precedence of or is smaller than the precedence of and.
in, false, and true are reserved words.
The old construction for k,v in t, where t is a table, is deprecated (although it is still supported). Use for k,v in pairs(t) instead.
When a literal string of the form [[...]] starts with a newline, this newline is ignored.
Upvalues in the form %var are obsolete; use external local variables instead.
庫的變動
Most library functions now are defined inside tables. There is a compatibility script (compat.lua) that redefines most of them as global names.
In the math library, angles are expressed in radians. With the compatibility script (compat.lua), functions still work in degrees.
The call function is deprecated. Use f(unpack(tab)) instead of call(f, tab) for unprotected calls, or the new pcall function for protected calls.
dofile does not handle errors, but simply propagates them.
dostring is deprecated. Use loadstring instead.
The read option w is obsolete.
The format option %n$ is obsolete.
API 上的改動
lua_open 再也不須要堆棧大小做爲參數(堆棧是動態的)。
lua_pushuserdata 已經被廢除了。使用 lua_newuserdata 或 lua_pushlightuserdata 來代替它。
Lua 完整語法參考
chunk ::= {stat [`;´]}
block ::= chunk
stat ::= varlist1 `=´ explist1 | functioncall | do block end | while exp do block end | repeat block until exp | if exp then block {elseif exp then block} [else block] end | return [explist1] | break | for Name `=´ exp `,´ exp [`,´ exp] do block end | for Name {`,´ Name} in explist1 do block end | function funcname funcbody | local function Name funcbody | local namelist [init]
funcname ::= Name {`.´ Name} [`:´ Name]
varlist1 ::= var {`,´ var}
var ::= Name | prefixexp `[´ exp `]´ | prefixexp `.´ Name
namelist ::= Name {`,´ Name}
init ::= `=´ explist1
explist1 ::= {exp `,´} exp
exp ::= nil | false | true | Number | Literal | function | prefixexp | tableconstructor | exp binop exp | unop exp
prefixexp ::= var | functioncall | `(´ exp `)´
functioncall ::= prefixexp args | prefixexp `:´ Name args
args ::= `(´ [explist1] `)´ | tableconstructor | Literal
function ::= function funcbody
funcbody ::= `(´ [parlist1] `)´ block end
parlist1 ::= Name {`,´ Name} [`,´ `...´] | `...´
tableconstructor ::= `{´ [fieldlist] `}´
fieldlist ::= field {fieldsep field} [fieldsep]
field ::= `[´ exp `]´ `=´ exp | name `=´ exp | exp
fieldsep ::= `,´ | `;´
binop ::= `+´ | `-´ | `´ | `/´ | `^´ | `..´ | `<´ | `<=´ | `>´ | `>=´ | `==´ | `~=´ | and | or
unop ::= `-´ | not 安全