lua的語句包括賦值、控制結構、函數調用、變量聲明。
lua的一個執行單元被稱為語句組。一個語句組就是一串語句段,它們會被循序執行。每個語句段可以以一個分號結束。在lua中並不強行要求一定以一個分號來結束一個語句,也可以用回車來作為一條語句的結束標識。
lua把一個語句組當做一個擁有不定參數的匿名函數。語句組內可以定義局部變量,接收參數,並且返回值。語句組可以被保存在一個文件中,也可以保存在宿主程序的一個字符串中。當一個語句組被執行時,首先它會被編譯成虛擬機中的指令序列,然後被虛擬機解釋運行這些指令。語句組也可以被編譯成二進制形式。用源代碼提供的程序和被編譯過的二進制形式的程序是可以相互替換的,lua會自動識別文件類型並做正確的處理。(具體編譯和轉換細節將會在後續的章節中講解)
賦值:lua允許多重賦值,例如:
local x, y = 10, 20; 此時x = 10, y = 20
local x, y = 10; 此時x = 10, y = nil
local x, y = 10, 20, 30; 此時x = 10, y = 20, 30這個值被捨棄掉。
多重賦值的原則,從左到右依次賦值。右值會對應左邊的個數,多余的會被捨棄,不足的用nil來填充。如果表達式列表以一個函數調用為結束,這個函數所返回的所有值都會在對齊操作之前被置入右值序列中。例如
function getFrame()
local x = 0;
local y = 0;
local w = 320;
local h = 568;
return x, y, w, h;
end
local x, y, w, h = getFrame() -- 此處的 x = 0, y = 0, w = 320, h = 568
賦值段首先會做運算所有的表達式,然後僅僅做賦值操作。例如
local a = {}
local i = 3
i , a[i] = i + 1, 20
這個例子會把a[3]設置為20, 而不會影響到a[4]。因為a[i]中的i在被賦值為4之前(i = 3時)就被拿出來了。
使用下面的表達式可以交換兩個值:
x, y = y, x
對於全局變量以及table中的域的賦值操作的含義可以通過metatable來改變。對變量下標指向的賦值,即t[i] = val等價於settable_event(t, i, val)。
對於全局變量的賦值x = val等價於_env.x = val, 這個又等價於settable_event(_env, x, val)。(settable_event函數在之前的章節中也有提到過,本章只是拿來舉例,不做具體的說明)
控制結構:在lua中的控制結構包含if、while、repeat、for。
while exp do block end
repeat block until exp
if exp then block {elseif exp then block} else block end
控制結構中的條件表達式可以返回任何值。false和nil兩者都被認為是假條件,除此之外的其他值被認為是真(數字0和空字符串也被認為是真)。
在repeat-until循環中,內部語句塊的結束點不是在until這個關鍵字處,它還包括了其他的條件表達式。因此,條件表達式中可以使用循環內部語句塊中的定義的局部變量。
return被用於從函數或者是語句組中返回值。函數和語句組可以返回不止一個值,可參考上面getFrame的例子。
break被用來結束while、repeat、for虛幻,它將忽略循環中下面的語句段的運行,break跳出最內層的循環。
可以忽略此括號內的內容(return 和 break 只能被卸載一個語句塊的最後一句。如果你真的需要從語句塊中間return 或是 break,你可以使用顯式的聲明一個內部語句塊。一般寫為do return end或是do break end, 可以這樣寫是因為現在return 或 break 都成了一個語句塊的最後一句了。)編碼過程中並不推薦這種寫法。
for循環有三種形式:
for v = var, limit, step do block end
var為起始值,limit為終止值, step為步長。所有三個控制表達式都只被運行一次,表達式的計算在循環開始之前。這些表達式的結果必須為數字。可以使用break用來退出for循環。循環變量v是一個循環內部的局部變量;當循環結束時,v則被釋放掉了,如果想在循環外部繼續使用這個值,則需要將這個值賦給一個更高層次的局部變量或全局變量。
迭代器:for namelist in explist do block end 通常為一下兩種用法
for k, v in pairs(explist) do block end
for k, v in ipairs(explist) do block end
explist為table類型。k,v為explist表中的key和value。explist只會被計算一次。它返回三個值,一個迭代器函數,一個狀態,一個迭代器的初始值。和上面一樣,迭代器也可以使用break來跳出for循環。如果想保留v,同樣也需要一個高層次的局部變量或全局變量來引用。
pairs和ipairs的區別:
pairs可以遍歷表中所有的key,並且除了迭代器本身以及遍歷表本身還可以返回nil。但是ipairs則不能返回nil,只能發揮數字0,如果遇到nil則退出。它只能遍歷到表中出現的第一個不是整數的key。
舉例說明:
local tab = { [3] = "test2", [6] = "test3", [4] = "test1" }
for k, v in ipairs(tab) do
print(k, v)
end
無任何輸出結果,原因是當key = 1時,value就是nil,此時就跳出循環不輸出任何值。
同樣的代碼,我們用pairs來輸出
local tab = { [3] = "test2", [6] = "test3", [4] = "test1" }
for k, v in pairs(tab) do
print(k, v)
end
結果為3 test2
6 test3
4 test1
我們將上面ipairs的例子改一下,將tab表中[3]改為[1]
local tab = { [1] = "test1", [6] = "test2", [4] = "test3" }
for k, v in ipairs(tab) do
print(k, v)
end
現在的輸出結果顯而易見就是 1 test1
表達式:lua基本的表達式包含算術操作符、關系操作符、邏輯操作符、字符串連接、table構造器、取長度操作符、函數等。
算術操作符和關系操作符在之前的章節中有做介紹,本文不做詳細的講解。
邏輯操作符包含and、or、not。與條件操作語句一樣,所有的邏輯操作符都將false和nil視為假,其他結果均為真。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
一個很實用的技巧:如果x為false或者nil則給x賦初始值v
x = x or v
等價於
if not x then
x = v
end
and的優先級比or高
在C語言中的三目運算符 a ? b : c
在lua中可以這樣實現 (a and b) or c
not的結果只返回false或者是true
print( not nil ) --> true
print( not false ) --> true
print( not 0 ) --> false
print( not not nil ) --> false
字符串連接:在lua中,兩個字符串連接可以使用(..) 兩個點,稱為字符串連接操作符。print( "hello" .. "world" ) --> helloworld
print( 4 .. 5 )將兩個number類型的數字用..進行連接,在lua執行時會將其自動轉換為字符串,輸出結果為45,但是注意,這個時候的45已經不是number類型而是string類型。
table構造器:構造器用於構建和初始化table的表達式。這是lua特有的表達式,也是lua種最有用、最通用的機制之一。最簡單的構造器為空構造器{},用於創建一張空表。我們通過構造器還可以初始化一些數據,數組初始化方式,如
days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }
我們來打印一下這個表
for i = 1, #days do
print(days[i]);
end
輸出結果為:
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
從輸出結果可以看出,days在構造後會將自動初始化,其中days[1]被初始化為"Sunday",以此類推。
lua中還提供了另一種特殊的語法用於初始化一張表,記錄初始化方式。如 a = { x = 10, y = 20 },其等價於: a = {}; a.x = 10; a.y = 20;
除了上面兩種構造方式之外,lua還提供了一種更為通用的方式,如:
opnames = { ["+"] = "add", ["-"] = "sub", ["*"] = "mul", ["/"] = "div" }
print(opnames["+"]) --> add
對於table的構造器,還有兩個需要了解的語法規則,如:
a = { [1] = "red", [2] = "green", [3] = "blue", }
這裡需要注意最後一個元素的後面仍然可以保留都好(,), 這一點類似於C語言中的枚舉。
a = { x = 10, y = 45; "one", "two", "three" }
可以看到上面的聲明中同事存在都好(,)和分號(;)兩種元素分隔符,這種寫法在lua中也是允許的。我們通常會將分號(;)用於分隔不同初始化類型的元素,如上例中分號之前的初始化為記錄初始化,而後面的則是數組初始化方式。
取長度操作符:上面我們有用到#,在lua中,#為取長度操作符。字符串的長度是它的字節數。table的長度被定義成一個整數下標n。它滿足t[n]不是nil而t[n+1]為nil;此外,如果t[1]為nil,n就可能是零。對於常規的數組,裡面從1到n放著一些非空的值的時候,它的長度就精確的為n,即最後一個值的下標。如果數組中有一個“空洞”(就是說,nil值被夾在非空值之間),那麼#t可能是指向任何一個是nil值的前一個位置的下標(就是說,任何一個nil值都有可能被當成數組的結束)。
優先級:lua中操作符的優先級從低到高的排序:
or and < > <= >= ~= == .. + - * / % not # - (unary) ^通常你可以使用括號來改變運算的次序。連接操作符(..)和冪操作符(^)是從右至左。其他所有的操作都是從左到右。
函數:在lua中,函數的寫法有以下兩種:
func = function(params) body end
function func(params) body end
這兩種寫法是可以相互轉換的,後者只是通過語法糖的簡化了寫法而已。但是函數構成並沒有變,仍然是函數名、參數和返回值。在lua中可以使用(...)來描述一組參數,但是必須放到參數列表的末尾。當一個函數被調用,如果函數沒有被定義為接收不定長參數,即在形參列表的末尾注明三個點(...),那麼實參列表就會被調整到形參列表的長度,變長參數函數不會調整實參列表。舉例說明:
function f( a, b ) end
f( 3 ) --> a = 3, b = nil
f( 3, 4 ) --> a = 3, b = 4
f( 3, 4, 5 ) --> a = 3, b = 4
和多重賦值一樣,多於的實參會被捨棄掉,少於的實參會使用nil填充
在函數中,可以使用return來返回。如果執行到函數末尾依舊沒有遇到任何return語句,函數就不會返回任何結果,也可以認為默認幫我們返回一個nil值。
冒號語法可以用來定義方法,也就是說函數可以有一個隱式的形參self。因此,如下寫法:
local meta = {}
function meta:log(params) body end
相當於:
meta.log(self, params) body end
對於函數具體使用和詳解,將會在後面的章節中介紹,本文只是講一些簡單的使用。