<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>


            Lua 5.1 參考手冊

            by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes

            云風 譯 www.codingnow.com

            @ssv | 漏洞目錄 | Lua性能優化技巧

            Copyright© 2006 Lua.org, PUC-Rio. All rights reserved.

            1 - 介紹

            Lua 是一個擴展式程序設計語言,它被設計成支持通用的過程式編程,并有相關數據描述的設施。 Lua 也能對面向對象編程,函數式編程,數據驅動式編程提供很好的支持。 它可以作為一個強大、輕量的腳本語言,供任何需要的程序使用。 Lua 以一個用 clean C 寫成的庫形式提供。(所謂 Clean C ,指的 ANSI C 和 C++ 中共通的一個子集)

            作為一個擴展式語言,Lua 沒有 "main" 程序的概念:它只能 嵌入 一個宿主程序中工作,這個宿主程序被稱作 embedding program 或簡稱為 host 。 宿主程序可以通過調用函數執行一小段 Lua 代碼,可以讀寫 Lua 變量,可以注入 C 函數讓 Lua 代碼調用。 這些擴展的 C 函數,可以大大的擴展了 Lua 可以處理事務的領域,這樣就可以訂制出各種語言, 而它們共享一個統一的句法格式的框架。 Lua 的官方發布版就包含了一個叫做 lua 的簡單的宿主程序,它用 Lua 庫提供了一個保證獨立的 Lua 解釋器。

            Lua 是一個自由軟件,它的使用許可決定了對它的使用過程一般沒有任何保證。 這份手冊中描述的東西的實現,可以在 Lua 的官方網站 www.lua.org 找到,

            跟其它的許多參考手冊一樣,這份文檔有些地方比較枯燥。 關于 Lua 的設計想法的探討,可以看看 Lua 網站上提供的技術論文。 有關用 Lua 編程的細節介紹,可以讀一下 Roberto 的書,Programming in Lua (Second Edition)

            2 - 語言

            這一節從詞法、語法、句法上描述 Lua 。 換句話說,這一節描述了哪些 token (符記)是有效的,它們如何被組合起來,這些組合方式有什么含義。

            關于語言的構成概念將用常見的擴展 BNF 表達式寫出。也就是這個樣子: {a} 意思是 0 或多個 a , [a] 意思是一個可選的 a 。 非最終的符號會保留原來的樣子,關鍵字則看起來像這樣 kword , 其它最終的符號則寫成 `=´ 。 完整的 Lua 語法可以在本手冊最后找到。

            2.1 - 詞法約定

            Lua 中用到的 名字(也稱作 標識符)可以是任何非數字開頭的字母、數字、下劃線組成的字符串。 這符合幾乎所有編程語言中關于名字的定義。 (字母的定義依賴于當前環境:系統環境中定義的字母表中的字母都可以被用于標識符。) 標識符用來命名變量,或作為表的域名。

            下面的關鍵字是保留的,不能用作名字:

                 and       break     do        else      elseif
                 end       false     for       function  if
                 in        local     nil       not       or
                 repeat    return    then      true      until     while
            

            Lua 是一個大小寫敏感的語言: and 是一個保留字,但是 AndAND 則是兩個不同的合法的名字。 一般約定,以下劃線開頭連接一串大寫字母的名字(比如 _VERSION)被保留用于 Lua 內部全局變量。

            下面這些是其它的 token :

                 +     -     *     /     %     ^     #
                 ==    ~=    <=    >=    <     >     =
                 (     )     {     }     [     ]
                 ;     :     ,     .     ..    ...
            
            

            字符串既可以用一對單引號引起,也可以是雙引號,里面還可以包含類似 C 的轉義符: '\a' (響鈴), '\b' (退格), '\f' (表單), '\n' (換行), '\r' (回車), '\t' (橫向制表), '\v' (縱向制表), '\\' (反斜杠), '\"' (雙引號), 以及 '\'' (單引號)。 而且,如果在一個反斜杠后跟了一個真正的換行符,其結果就是在字符串中產生一個換行符。 我們還可以用反斜杠加數字的形式 \ddd 來描述一個字符。這里, ddd 是一串最多三位的十進制數字。(注意,如果需要在這種描述方法后接一個是數字的字符, 那么反斜杠后必須寫滿三個數字。)Lua 中的字符串可以包含任何 8 位的值。包括用 '\0' 表示的零。

            只有在你需要把不同的引號、換行、反斜杠、或是零結束符這些字符置入字符串時, 你才必須使用轉義符。別的任何字符都可以直接寫在文本里。(一些控制符可以會影響文件系統造成某些問題, 但是不會引起 Lua 的任何問題。)

            字符串還可以用一種長括號括起來的方式定義。 我們把兩個正的方括號間插入 n 個等號定義為第 n 級正長括號。 就是說,0 級正的長括號寫作 [[ , 一級正的長括號寫作 [=[ ,如此等等。 反的長擴展也作類似定義; 舉個例子,4 級反的長括號寫作 ]====] 。 一個長字符串可以由任何一級的正的長括號開始,而由第一個碰到的同級反的長括號結束。 整個詞法分析過程將不受分行限制,不處理任何轉意符,并且忽略掉任何不同級別的長括號。 這種方式描述的字符串可以包含任何東西,當然特定級別的反長括號除外。

            另一個約定是,當正的長括號后面立即跟了一個換行符, 這個換行符就不包含在這個字符串內。 舉個例子,假設一個系統使用 ASCII 碼 (這時,'a' 編碼為 97 ,換行符編碼為 10 ,'1' 編碼為 49 ), 下面五種方式描述了完全相同的字符串:

                 a = 'alo\n123"'
                 a = "alo\n123\""
                 a = '\97lo\10\04923"'
                 a = [[alo
                 123"]]
                 a = [==[
                 alo
                 123"]==]
            

            數字常量可以分兩部分寫,十進制底數部分和十進制的指數部分。指數部分是可選的。 Lua 也支持十六進制整數常量,只需要在前面加上前綴 0x 。 下面是一些合法的數字常量的例子:

            
                 3   3.0   3.1416   314.16e-2   0.31416E1   0xff   0x56
            

            注釋可以在除字符串內的任何地方是以兩橫 (--) 開始。 如果跟在兩橫后面的不是一個長括號,這就是一個短注釋,它的作用范圍直到行末; 否則就是一個長注釋,其作用范圍直到遇到反的長括號。 長注釋通常被用來臨時屏蔽代碼塊。

            2.2 - 值與類型

            Lua 是一種 動態類型語言。 這意味著變量沒有類型,只有值才有類型。 語言中不存在類型定義。而所有的值本身攜帶它們自己的類型信息。

            Lua 中的所有值都是一致 (first-class) 的。 這意味著所有的值都可以被放在變量里,當作參數傳遞到另一個函數中,并被函數作為結果返回。

            Lua 中有八種基本類型: nil, boolean, number, string, function, userdata, thread, and table. Nil 類型只有一種值 nil ,它的主要用途用于標表識和別的任何值的差異; 通常,當需要描述一個無意義的值時會用到它。 Boolean 類型只有兩種值:falsetruenilfalse 都能導致條件為假;而另外所有的值都被當作真。 Number 表示實數(雙精度浮點數)。 (編譯一個其它內部數字類型的 Lua 解釋器是件很容易的事;比如把內部數字類型改作 單精度浮點數或長整型。參見文件 luaconf.h 。) String 表示一串字符的數組。 Lua 是 8-bit clean 的: 字符串可以包含任何 8 位字符, 包括零結束符 ('\0') (參見 §2.1)。

            Lua 可以調用(和處理)用 Lua 寫的函數以及用 C 寫的函數(參見 §2.5.8).

            userdata 類型用來將任意 C 數據保存在 Lua 變量中。 這個類型相當于一塊原生的內存,除了賦值和相同性判斷,Lua 沒有為之預定義任何操作。 然而,通過使用 metatable (元表) ,程序員可以為 userdata 自定義一組操作 (參見 §2.8)。 userdata 不能在 Lua 中創建出來,也不能在 Lua 中修改。這樣的操作只能通過 C API。 這一點保證了宿主程序完全掌管其中的數據。

            thread 類型用來區別獨立的執行線程,它被用來實現 coroutine (協同例程)(參見 §2.11)。 不要把 Lua 線程跟操作系統的線程搞混。 Lua 可以在所有的系統上提供對 coroutine 的支持,即使系統并不支持線程。

            table 類型實現了一個關聯數組。也就是說, 數組可以用任何東西(除了nil)做索引,而不限于數字。 table 可以以不同類型的值構成;它可以包含所有的類型的值(除 nil 外)。 table 是 lua 中唯一的一種數據結構;它可以用來描述原始的數組、符號表、集合、 記錄、圖、樹、等等。 用于表述記錄時,lua 使用域名作為索引。 語言本身采用一種語法糖,支持以 a.name 的形式表示 a["name"]。 有很多形式用于在 lua 中創建一個 table (參見 §2.5.7)。

            跟索引一樣, table 每個域中的值也可以是任何類型(除 nil外)。 特別的,因為函數本身也是值,所以 table 的域中也可以放函數。 這樣 table 中就可以有一些 methods 了 (參見see §2.5.9)。

            table, function ,thread ,和 (full) userdata 這些類型的值是所謂的對象: 變量本身并不會真正的存放它們的值,而只是放了一個對對象的引用。 賦值,參數傳遞,函數返回,都是對這些對象的引用進行操作; 這些操作不會做暗地里做任何性質的拷貝。

            庫函數 type 可以返回一個描述給定值的類型的字符串。

            2.2.1 - 強制轉換

            Lua 提供運行時字符串到數字的自動轉換。 任何對字符串的數學運算操作都會嘗試用一般的轉換規則把這個字符串轉換成一個數字。 相反,無論何時,一個數字需要作為字符串來使用時,數字都會以合理的格式轉換為字符串。 需要完全控制數字怎樣轉換為字符串,可以使用字符串庫中的 format 函數 (參見 string.format)。

            2.3 - 變量

            寫上變量的地方意味著當以其保存的值來替代之。 Lua 中有三類變量:全局變量,局部變量,還有 table 的域。

            一個單一的名字可以表示一個全局變量,也可以表示一個局部變量 (或者是一個函數的參數,這是一種特殊形式的局部變量):

            
            	var ::= Name
            

            Name 就是 §2.1 中所定義的標識符。

            任何變量都被假定為全局變量,除非顯式的以 local 修飾定義 (參見 §2.4.7)。 局部變量有其作用范圍: 局部變量可以被定義在它作用范圍中的函數自由使用 (參見 §2.6)。

            在變量的首次賦值之前,變量的值均為 nil

            方括號被用來對 table 作索引:

            	var ::= prefixexp `[´ exp `]´
            

            對全局變量以及 table 域之訪問的含義可以通過 metatable 來改變。 以取一個變量下標指向的量 t[i] 等價于調用 gettable_event(t,i)。 (參見 §2.8 ,有一份完整的關于 gettable_event 函數的說明。 這個函數并沒有在 lua 中定義出來,也不能在 lua 中調用。 這里我們把它列出來只是方便說明。)

            var.Name 這種語法只是一個語法糖,用來表示 var["Name"]

            	var ::= prefixexp `.´ Name
            

            所有的全局變量都是放在一個特定 lua table 的諸個域中,這個特定的 table 叫作 environment (環境)table 或者簡稱為 環境 (參見 §2.9)。 每個函數都有對一個環境的引用, 所以一個函數中可見的所有全局變量都放在這個函數所引用的環境表(environment table)中。 當一個函數被創建出來,它會從創建它的函數中繼承其環境,你可以調用 getfenv 取得其環境。 如果想改變環境,可以調用 setfenv。 (對于 C 函數,你只能通過 debug 庫來改變其環境; 參見 §5.9)。

            對一個全局變量 x 的訪問 等價于 _env.x,而這又可以等價于

                 gettable_event(_env, "x")
            

            這里,_env 是當前運行的函數的環境。 (函數 gettable_event 的完整說明參見 §2.8。 這個函數并沒有在 lua 中定義出來,也不能調用。 當然,_env 這個變量也同樣沒有在 Lua 中定義出來。 我們在這里使用它們,僅僅只是方便解釋而已。)

            2.4 - 語句段(Statement)

            Lua 支持慣例形式的語句段,它和 Pascal 或是 C 很相象。 這個集合包括賦值,控制結構,函數調用,還有變量聲明。

            2.4.1 - Chunk(語句組)

            Lua 的一個執行單元被稱作 chunk。 一個 chunk 就是一串語句段,它們會被循序的執行。 每個語句段可以以一個分號結束:

            	chunk ::= {stat [`;´]}
            
            

            這兒不允許有空的語句段,所以 ';;' 是非法的。

            lua 把一個 chunk 當作一個擁有不定參數的匿名函數 (參見 §2.5.9)處理。 正是這樣,chunk 內可以定義局部變量,接收參數,并且返回值。

            chunk 可以被保存在一個文件中,也可以保存在宿主程序的一個字符串中。 當一個 chunk 被執行,首先它會被預編譯成虛擬機中的指令序列, 然后被虛擬機解釋運行這些指令。

            chunk 也可以被預編譯成二進制形式;細節參考程序 luac。 用源碼形式提供的程序和被編譯過的二進制形式的程序是可以相互替換的; Lua 會自動識別文件類型并做正確的處理。

            2.4.2 - 語句塊

            語句塊是一列語句段;從語法上來說,一個語句塊跟一個 chunk 相同:

            	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
            

            可以用來交換 xy 中的值。

            對全局變量以及 table 中的域的賦值操作的含義可以通過 metatable 來改變。 對變量下標指向的賦值,即 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 - 控制結構

            ifwhile、以及 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)。

            控制結構中的條件表達式可以返回任何值。 falsenil 兩者都被認為是假條件。 所有不同于 nilfalse 的其它值都被認為是真 (特別需要注意的是,數字 0 和空字符串也被認為是真)。

            repeatuntil 循環中, 內部語句塊的結束點不是在 until 這個關鍵字處, 它還包括了其后的條件表達式。 因此,條件表達式中可以使用循環內部語句塊中的定義的局部變量。

            return 被用于從函數或是 chunk(其實它就是一個函數)中 返回值。 函數和 chunk 可以返回不只一個值, 所以 return 的語法為

            
            	stat ::= return [explist1]
            

            break 被用來結束 whilerepeat、或 for 循環, 它將忽略掉循環中下面的語句段的運行:

            	stat ::= break
            

            break 跳出最內層的循環。

            returnbreak 只能被寫在一個語句塊的最后一句。 如果你真的需要從語句塊的中間 return 或是 break , 你可以使用顯式的聲名一個內部語句塊。 一般寫作 do return end 或是 do break end, 可以這樣寫是因為現在 returnbreak 都成了一個語句塊的最后一句了。

            2.4.5 - For 語句

            for 有兩種形式:一種是數字形式,另一種是一般形式。

            數字形式的 for 循環,通過一個數學運算不斷的運行內部的代碼塊。 下面是它的語法:

            	stat ::= for Name `=´ exp `,´ exp [`,´ exp] do block end
            
            

            block 將把 name 作循環變量。從第一個 exp 開始起,直到第二個 exp 的值為止,其步長為 第三個 exp 。 更確切的說,一個 for 循環看起來是這個樣子

                 for v = 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
                     local v = var
            
                     block
                     var = var + step
                   end
                 end
            

            注意下面這幾點:

            一般形式的 for 通過一個叫作迭代器(iterators)的函數工作。 每次迭代,迭代器函數都會被調用以產生一個新的值, 當這個值為 nil 時,循環停止。 一般形式的 for 循環的語法如下:

            	stat ::= for namelist in explist1 do block end
            	namelist ::= Name {`,´ Name}
            
            

            for 語句好似這樣

                 for var_1, ···, var_n in explist do block end
            
            

            它等價于這樣一段代碼:

                 do
                   local f, s, var = explist
                   while true do
                     local var_1, ···, var_n = f(s, var)
                     var = var_1
            
                     if var == nil then break end
                     block
                   end
                 end
            

            注意以下幾點:

            2.4.6 - 把函數調用作為語句段

            為了允許使用可能的副作用, 函數調用可以被作為一個語句段執行:

            	stat ::= functioncall
            

            在這種情況下,所有的返回值都被舍棄。 函數調用在 §2.5.8 中解釋。

            2.4.7 - 局部變量聲名

            局部變量可以在語句塊中任何地方聲名。 聲名可以包含一個初始化賦值操作:

            
            	stat ::= local namelist [`=´ explist1]
            

            如果有的話,初始化賦值操作的行為等同于賦值操作(參見 §2.4.3)。 否則,所有的變量將被初始化為 nil

            一個 chunk 同時也是一個語句塊(參見 §2.4.1), 所以局部變量可以放在 chunk 中那些顯式注明的語句塊之外。 這些局部變量的作用范圍從聲明起一直延伸到 chunk 末尾。

            局部變量的可見規則在 §2.6 中解釋。

            2.5 - 表達式

            Lua 中有這些基本表達式:

            	exp ::= prefixexp
            	exp ::= nil | false | true
            
            	exp ::= Number
            	exp ::= String
            	exp ::= function
            	exp ::= tableconstructor
            	exp ::= `...´
            	exp ::= exp binop exp
            	exp ::= unop exp
            	prefixexp ::= var | functioncall | `(´ exp `)´
            

            數字和字符串在 §2.1 中解釋; 變量在 §2.3 中解釋; 函數定義在 §2.5.9 中解釋; 函數調用在 §2.5.8 中解釋; table 的構造在 §2.5.7 中解釋; 可變參數的表達式寫作三個點 ('...') ,它只能被用在有可變參數的函數中; 這些在 §2.5.9 中解釋。

            二元操作符包含有數學運算操作符(參見 §2.5.1), 比較操作符(參見 §2.5.2),邏輯操作符(參見 §2.5.3), 以及連接操作符(參見 §2.5.4)。 一元操作符包括負號(參見see §2.5.1), 取反 not(參見 §2.5.3), 和取長度操作符(參見 §2.5.5)。

            函數調用和可變參數表達式都可以放在多重返回值中。 如果表達式作為一個獨立語句段出現(參見 §2.4.6) (這只能是一個函數調用), 它們的返回列表將被對齊到零個元素,也就是忽略所有返回值。 如果表達式用于表達式列表的最后(或者是唯一)的元素, 就不會有任何的對齊操作(除非函數調用用括號括起來)。 在任何其它的情況下,Lua 將把表達式結果看成單一元素, 忽略除第一個之外的任何值。

            這里有一些例子:

                 f()                -- 調整到 0 個結果
                 g(f(), x)          -- f() 被調整到一個結果
                 g(x, f())          -- g 被傳入 x 加上所有 f() 的返回值
                 a,b,c = f(), x     -- f() 被調整到一個結果 ( c 在這里被賦為 nil )
                 a,b = ...          -- a 被賦值為可變參數中的第一個,
                                    -- b 被賦值為第二個 (如果可變參數中并沒有對應的值,
            						-- 這里 a 和 b 都有可能被賦為 nil)
                 
                 a,b,c = x, f()     -- f() 被調整為兩個結果
                 a,b,c = f()        -- f() 被調整為三個結果
                 return f()         -- 返回 f() 返回的所有結果
                 return ...         -- 返回所有從可變參數中接收來的值
                 return x,y,f()     -- 返回 x, y, 以及所有 f() 的返回值
                 {f()}              -- 用 f() 的所有返回值創建一個列表
                 {...}              -- 用可變參數中的所有值創建一個列表
                 {f(), nil}         -- f() 被調整為一個結果
            

            被括號括起來的表達式永遠被當作一個值。所以, (f(x,y,z)) 即使 f 返回多個值,這個表達式永遠是一個單一值。 ((f(x,y,z)) 的值是 f 返回的第一個值。如果 f 不返回值的話,那么它的值就是 nil 。)

            2.5.1 - 數學運算操作符

            Lua 支持常見的數學運算操作符: 二元操作 + (加法), - (減法),* (乘法), / (除法), % (取模),以及 ^ (冪); 和一元操作 - (取負)。 如果對數字操作,或是可以轉換為數字的字符串(參見 §2.2.1), 所有這些操作都依賴它通常的含義。 冪操作可以對任何冪值都正常工作。比如, x^(-0.5) 將計算出 x 平方根的倒數。 取模操作被定義為

                 a % b == a - math.floor(a/b)*b
            

            這就是說,其結果是商相對負無窮圓整后的余數。(譯注:負數對正數取模的結果為正數)

            2.5.2 - 比較操作符

            Lua 中的比較操作符有

                 ==    ~=    <     >     <=    >=
            

            這些操作的結果不是 false 就是 true

            等于操作 (==) 首先比較操作數的類型。 如果類型不同,結果就是 false。 否則,繼續比較值。 數字和字符串都用常規的方式比較。 對象 (table ,userdata ,thread ,以及函數)以引用的形式比較: 兩個對象只有在它們指向同一個東西時才認為相等。 每次你創建一個新對象(一個 table 或是 userdata ,thread 函數), 它們都各不相同,即不同于上次創建的東西。

            你可以改變 Lua 比較 table 和 userdata 的方式,這需要使用 "eq" 這個原方法 (參見 §2.8)。

            §2.2.1 中提及的轉換規則并不作用于比較操作。 所以, "0"==0 等于 false, 而且 t[0]t["0"] 描述的是 table 中不同的域。

            操作符 ~= 完全等價于 (==) 操作的反值。

            大小比較操作以以下方式進行。 如果參數都是數字,那么就直接做數字比較。 否則,如果參數都是字符串,就用字符串比較的方式進行。 再則,Lua 就試著調用 "lt" 或是 "le" 元方法 (參見 §2.8)。

            2.5.3 - 邏輯操作符

            Lua 中的邏輯操作符有 and, or, 以及 not。 和控制結構(參見 §2.4.4)一樣, 所有的邏輯操作符把 falsenil 都作為假, 而其它的一切都當作真。

            取反操作 not 總是返回 falsetrue 中的一個。 與操作符 and 在第一個參數為 falsenil 時 返回這第一個參數; 否則,and 返回第二個參數。 或操作符 or 在第一個參數不為 nil 也不為 false 時, 返回這第一個參數,否則返回第二個參數。 andor 都遵循短路規則; 也就是說,第二個操作數只在需要的時候去求值。 這里有一些例子:

                 10 or 20            --> 10
                 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 - 取長度操作符

            取長度操作符寫作一元操作 #。 字符串的長度是它的字節數(就是以一個字符一個字節計算的字符串長度)。

            table t 的長度被定義成一個整數下標 n 。 它滿足 t[n] 不是 nilt[n+1]nil; 此外,如果 t[1]niln 就可能是零。 對于常規的數組,里面從 1 到 n 放著一些非空的值的時候, 它的長度就精確的為 n,即最后一個值的下標。 如果數組有一個“空洞” (就是說,nil 值被夾在非空值之間), 那么 #t 可能是指向任何一個是 nil 值的前一個位置的下標 (就是說,任何一個 nil 值都有可能被當成數組的結束)。

            2.5.6 - 優先級

            Lua 中操作符的優先級寫在下表中,從低到高優先級排序:

                 or
                 and
                 <     >     <=    >=    ~=    ==
                 ..
                 +     -
                 *     /     %
                 not   #     - (unary)
                 ^
            

            通常,你可以用括號來改變運算次序。 連接操作符 ('..') 和冪操作 ('^') 是從右至左的。 其它所有的操作都是從左至右。

            2.5.7 - Table 構造

            table 構造子是一個構造 table 的表達式。 每次構造子被執行,都會構造出一個新的 table 。 構造子可以被用來構造一個空的 table, 也可以用來構造一個 table 并初始化其中的一些域。 一般的構造子的語法如下

            	tableconstructor ::= `{´ [fieldlist] `}´
            	fieldlist ::= field {fieldsep field} [fieldsep]
            	field ::= `[´ exp `]´ `=´ exp | Name `=´ exp | exp
            	fieldsep ::= `,´ | `;´
            
            

            每個形如 [exp1] = exp2 的域向 table 中增加新的一項, 其鍵值為 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 t = {}
                   t[f(1)] = g
                   t[1] = "x"         -- 1st exp
                   t[2] = "y"         -- 2nd exp
                   t.x = 1            -- t["x"] = 1
                   t[3] = f(x)        -- 3rd exp
                   t[30] = 23
                   t[4] = 45          -- 4th exp
                   a = t
                 end
            

            如果表單中最后一個域的形式是 exp , 而且其表達式是一個函數調用或者是一個可變參數, 那么這個表達式所有的返回值將連續的進入列表 (參見 §2.5.8)。 為了避免這一點,你可以用括號把函數調用(或是可變參數)括起來 (參見 §2.5)。

            初始化域表可以在最后多一個分割符, 這樣設計可以方便由機器生成代碼。

            2.5.8 - 函數調用

            Lua 中的函數調用的語法如下:

            	functioncall ::= prefixexp args
            
            

            函數調用時,第一步,prefixexp 和 args 先被求值。 如果 prefixexp 的值的類型是 function, 那么這個函數就被用給出的參數調用。 否則 prefixexp 的元方法 "call" 就被調用, 第一個參數就是 prefixexp 的值,跟下來的是原來的調用參數 (參見 §2.8)。

            這樣的形式

            	functioncall ::= prefixexp `:´ Name args
            

            可以用來調用 "方法"。 這是 Lua 支持的一種語法糖。像 v:name(args) 這個樣子,被解釋成 v.name(v,args), 這里 v 只會被求值一次。

            參數的語法如下:

            	args ::= `(´ [explist1] `)´
            
            	args ::= tableconstructor
            	args ::= String
            

            所有參數的表達式求值都在函數調用之前。 這樣的調用形式 f{fields} 是一種語法糖用于表示 f({fields}); 這里指參數列表是一個單一的新創建出來的列表。 而這樣的形式 f'string' (或是 f"string" 亦或是 f[[string]]) 也是一種語法糖,用于表示 f('string'); 這里指參數列表是一個單獨的字符串。

            因為表達式語法在 Lua 中比較自由, 所以你不能在函數調用的 '(' 前換行。 這個限制可以避免語言中的一些歧義。 比如你這樣寫

                 a = f
                 (g).x(a)
            

            Lua 將把它當作一個單一語句段, a = f(g).x(a) 。 因此,如果你真的想作為成兩個語句段,你必須在它們之間寫上一個分號。 如果你真的想調用 f, 你必須從 (g) 前移去換行。

            這樣一種調用形式:return functioncall 將觸發一個尾調用。 Lua 實現了適當的尾部調用(或是適當的尾遞歸): 在尾調用中, 被調用的函數重用調用它的函數的堆棧項。 因此,對于程序執行的嵌套尾調用的層數是沒有限制的。 然而,尾調用將刪除調用它的函數的任何調試信息。 注意,尾調用只發生在特定的語法下, 這時, return 只有單一函數調用作為參數; 這種語法使得調用函數的結果可以精確返回。 因此,下面這些例子都不是尾調用:

                 return (f(x))        -- 返回值被調整為一個
                 return 2 * f(x)
                 return x, f(x)       -- 最加若干返回值
                 f(x); return         -- 無返回值
                 return x or f(x)     -- 返回值被調整為一個
            

            2.5.9 - 函數定義

            函數定義的語法如下:

            	function ::= function funcbody
            	funcbody ::= `(´ [parlist1] `)´ block end
            
            

            另外定義了一些語法糖簡化函數定義的寫法:

            	stat ::= function funcname funcbody
            	stat ::= local function Name funcbody
            	funcname ::= Name {`.´ Name} [`:´ Name]
            
            

            這樣的寫法:

                 function f () body end
            

            被轉換成

                 f = function () body end
            

            這樣的寫法:

                 function t.a.b.c.f () body end
            

            被轉換成

                 t.a.b.c.f = function () body end
            

            這樣的寫法:

                 local function f () body end
            

            被轉換成

                 local f; f = function () body end
            

            注意,并不是轉換成

            
                 local f = function () body end
            

            (這個差別只在函數體內需要引用 f 時才有。)

            一個函數定義是一個可執行的表達式, 執行結果是一個類型為 function 的值。 當 Lua 預編譯一個 chunk 的時候, chunk 作為一個函數,整個函數體也就被預編譯了。 那么,無論何時 Lua 執行了函數定義, 這個函數本身就被實例化了(或者說是關閉了)。 這個函數的實例(或者說是 closure(閉包)) 是表達式的最終值。 相同函數的不同實例有可能引用不同的外部局部變量, 也可能擁有不同的環境表。

            形參(函數定義需要的參數)是一些由實參(實際傳入參數)的值初始化的局部變量:

            	parlist1 ::= namelist [`,´ `...´] | `...´
            

            當一個函數被調用, 如果函數沒有被定義為接收不定長參數,即在形參列表的末尾注明三個點 ('...'), 那么實參列表就會被調整到形參列表的長度, 變長參數函數不會調整實參列表; 取而代之的是,它將把所有額外的參數放在一起通過變長參數表達式傳遞給函數, 其寫法依舊是三個點。 這個表達式的值是一串實參值的列表,看起來就跟一個可以返回多個結果的函數一樣。 如果一個變長參數表達式放在另一個表達式中使用,或是放在另一串表達式的中間, 那么它的返回值就會被調整為單個值。 若這個表達式放在了一系列表達式的最后一個,就不會做調整了(除非用括號給括了起來)。

            我們先做如下定義,然后再來看一個例子:

                 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, ... -->  (nothing)
                 g(3, 4)          a=3, b=4,   ... -->  (nothing)
                 g(3, 4, 5, 8)    a=3, b=4,   ... -->  5  8
                 g(5, r())        a=5, b=1,   ... -->  2  3
            
            

            結果由 return 來返回(參見 §2.4.4)。 如果執行到函數末尾依舊沒有遇到任何 return 語句, 函數就不會返回任何結果。

            冒號語法可以用來定義方法, 就是說,函數可以有一個隱式的形參 self。 因此,如下寫法:

            
                 function t.a.b.c:f (params) body end
            

            是這樣一種寫法的語法糖:

                 t.a.b.c.f = function (self, params) body end
            
            

            2.6 - 可視規則

            Lua 是一個有詞法作用范圍的語言。 變量的作用范圍開始于聲明它們之后的第一個語句段, 結束于包含這個聲明的最內層語句塊的結束點。 看下面這些例子:

                 x = 10                -- 全局變量
                 do                    -- 新的語句塊
                   local x = x         -- 新的一個 'x', 它的值現在是 10
                   print(x)            --> 10
                   x = x+1
                   do                  -- 另一個語句塊
                     local x = x+1     -- 又一個 'x'
                     print(x)          --> 12
                   end
                   print(x)            --> 11
                 end
                 print(x)              --> 10  (取到的是全局的那一個)
            
            

            注意這里,類似 local x = x 這樣的聲明, 新的 x 正在被聲明,但是還沒有進入它的作用范圍, 所以第二個 x 指向的是外面一層的變量。

            因為有這樣一個詞法作用范圍的規則, 所以可以在函數內部自由的定義局部變量并使用它們。 當一個局部變量被更內層的函數中使用的時候, 它被內層函數稱作 upvalue(上值),或是 外部局部變量

            注意,每次執行到一個 local 語句都會定義出一個新的局部變量。 看看這樣一個例子:

                 a = {}
                 local x = 20
                 for i=1,10 do
                   local y = 0
                   a[i] = function () y=y+1; return x+y end
                 end
            

            這個循環創建了十個 closure(這指十個匿名函數的實例)。 這些 closure 中的每一個都使用了不同的 y 變量, 而它們又共享了同一份 x

            2.7 - 錯誤處理

            因為 Lua 是一個嵌入式的擴展語言, 所有的 Lua 動作都是從宿主程序的 C 代碼調用 Lua 庫 (參見 lua_pcall)中的一個函數開始的。 在 Lua 編譯或運行的任何時候發生了錯誤,控制權都會交還給 C , 而 C 可以來做一些恰當的措施(比如打印出一條錯誤信息)。

            Lua 代碼可以顯式的調用 error 函數來產生一條錯誤。 如果你需要在 Lua 中捕獲發生的錯誤, 你可以使用 pcall 函數。

            2.8 - Metatable(元表)

            Lua 中的每個值都可以用一個 metatable。 這個 metatable 就是一個原始的 Lua table , 它用來定義原始值在特定操作下的行為。 你可以通過在 metatable 中的特定域設一些值來改變擁有這個 metatable 的值 的指定操作之行為。 舉例來說,當一個非數字的值作加法操作的時候, Lua 會檢查它的 metatable 中 "__add" 域中的是否有一個函數。 如果有這么一個函數的話,Lua 調用這個函數來執行一次加法。

            我們叫 metatable 中的鍵名為 事件 (event) ,把其中的值叫作 元方法 (metamethod)。 在上個例子中,事件是 "add" 而元方法就是那個執行加法操作的函數。

            你可以通過 getmetatable 函數來查詢到任何一個值的 metatable。

            你可以通過 setmetatable 函數來替換掉 table 的 metatable 。 你不能從 Lua 中改變其它任何類型的值的 metatable (使用 debug 庫例外); 要這樣做的話必須使用 C API 。

            每個 table 和 userdata 擁有獨立的 metatable (當然多個 table 和 userdata 可以共享一個相同的表作它們的 metatable); 其它所有類型的值,每種類型都分別共享唯一的一個 metatable。 因此,所有的數字一起只有一個 metatable ,所有的字符串也是,等等。

            一個 metatable 可以控制一個對象做數學運算操作、比較操作、連接操作、取長度操作、取下標操作時的行為, metatable 中還可以定義一個函數,讓 userdata 作垃圾收集時調用它。 對于這些操作,Lua 都將其關聯上一個被稱作事件的指定健。 當 Lua 需要對一個值發起這些操作中的一個時, 它會去檢查值中 metatable 中是否有對應事件。 如果有的話,鍵名對應的值(元方法)將控制 Lua 怎樣做這個操作。

            metatable 可以控制的操作已在下面列出來。 每個操作都用相應的名字區分。 每個操作的鍵名都是用操作名字加上兩個下劃線 '__' 前綴的字符串; 舉例來說,"add" 操作的鍵名就是字符串 "__add"。 這些操作的語義用一個 Lua 函數來描述解釋器如何執行更為恰當。

            這里展示的用 Lua 寫的代碼僅作解說用; 實際的行為已經硬編碼在解釋器中,其執行效率要遠高于這些模擬代碼。 這些用于描述的的代碼中用到的函數 ( rawgettonumber ,等等。) 都可以在 §5.1 中找到。 特別注意,我們使用這樣一個表達式來從給定對象中提取元方法

                 metatable(obj)[event]
            

            這個應該被解讀作

                 rawget(getmetatable(obj) or {}, event)
            

            這就是說,訪問一個元方法不再會觸發任何的元方法, 而且訪問一個沒有 metatable 的對象也不會失敗(而只是簡單返回 nil)。

            2.9 - 環境

            類型為 thread ,function ,以及 userdata 的對象,除了 metatable 外還可以用另外一個與之關聯的被稱作 它們的環境的一個表, 像 metatable 一樣,環境也是一個常規的 table ,多個對象可以共享 同一個環境。

            userdata 的環境在 Lua 中沒有意義。 這個東西只是為了在程序員想把一個表關聯到一個 userdata 上時提供便利。

            關聯在線程上的環境被稱作全局環境。 全局環境被用作它其中的線程以及線程創建的非嵌套函數 (通過 loadfileloadstring 或是 load )的缺省環境。 而且它可以被 C 代碼直接訪問(參見 §3.3)。

            關聯在 C 函數上的環境可以直接被 C 代碼訪問(參見 §3.3)。 它們會作為這個 C 函數中創建的其它函數的缺省環境。

            關聯在 Lua 函數上的環境用來接管在函數內對全局變量(參見 §2.3)的所有訪問。 它們也會作為這個函數內創建的其它函數的缺省環境。

            你可以通過調用 setfenv 來改變一個 Lua 函數 或是正在運行中的線程的環境。 而想操控其它對象(userdata、C 函數、其它線程)的環境的話,就必須使用 C API 。

            2.10 - 垃圾收集

            Lua 提供了一個自動的內存管理。 這就是說你不需要關心創建新對象的分配內存操作,也不需要在這些對象不再需要時的主動釋放內存。 Lua 通過運行一個垃圾收集器來自動管理內存,以此一遍又一遍的回收死掉的對象 (這是指 Lua 中不再訪問的到的對象)占用的內存。 Lua 中所有對象都被自動管理,包括: table, userdata、 函數、線程、和字符串。

            Lua 實現了一個增量標記清除的收集器。 它用兩個數字來控制垃圾收集周期: garbage-collector pausegarbage-collector step multiplier

            garbage-collector pause 控制了收集器在開始一個新的收集周期之前要等待多久。 隨著數字的增大就導致收集器工作工作的不那么主動。 小于 1 的值意味著收集器在新的周期開始時不再等待。 當值為 2 的時候意味著在總使用內存數量達到原來的兩倍時再開啟新的周期。

            step multiplier 控制了收集器相對內存分配的速度。 更大的數字將導致收集器工作的更主動的同時,也使每步收集的尺寸增加。 小于 1 的值會使收集器工作的非常慢,可能導致收集器永遠都結束不了當前周期。 缺省值為 2 ,這意味著收集器將以內存分配器的兩倍速運行。

            你可以通過在 C 中調用 lua_gc 或是在 Lua 中調用 collectgarbage 來改變這些數字。 兩者都接受百分比數值(因此傳入參數 100 意味著實際值 1 )。 通過這些函數,你也可以直接控制收集器(例如,停止或是重啟)。

            2.10.1 - 垃圾收集的元方法

            使用 C API , 你可以給 userdata (參見 §2.8)設置一個垃圾收集的元方法。 這個元方法也被稱為結束子。 結束子允許你用額外的資源管理器和 Lua 的內存管理器協同工作 (比如關閉文件、網絡連接、或是數據庫連接,也可以說釋放你自己的內存)。

            一個 userdata 可被回收,若它的 metatable 中有 __gc 這個域 , 垃圾收集器就不立即收回它。 取而代之的是,Lua 把它們放到一個列表中。 最收集結束后,Lua 針對列表中的每個 userdata 執行了下面這個函數的等價操作:

                 function gc_event (udata)
                   local h = metatable(udata).__gc
             
            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线