天天看點

Lua腳本之文法基礎快速入門

         Lua的文法基礎超級簡單,非常易于上手,下面總結一些學習過程中的Lua文法基礎:

       在開始學習之前,先介紹一些最基本的概念,在Lua中具有一個代碼塊的概念,每個函數或者for循環等都是一個代碼塊。在Lua中,用 “- - ”來标記該行的注釋,使用“- - [ [” 和   “ - - ] ] ”之間括起來的部分進行塊注釋。如下所示:

-- 行注釋,僅僅注釋目前行
for idx = 1, 10 do  --在代碼之後進行行注釋
     print("idx=",idx);  
end
--[[
塊注釋,上邊的for循環結構跟end結合起來就是一個Lua中常見的代碼塊結構。
--]]
           

         另外, Lua中支援的算術運算符有:+、-、*、/,即加、減、乘、除;支援的關系運算符有:==、~=(不等于)、<、>、<=、>=;支援的邏輯運算符有:and、or、not。需要注意的是,在Lua中,and邏輯運算符如果第一個參數是false,則傳回false,不會執行第二個參數的代碼(即使第二個參數是一個錯誤的表達式,也能順利運作);如果第一個參數是true,傳回第二個參數的值。 同理,or邏輯運算符如果第一個參數不是false,則傳回第一個參數的值(不會執行第二個參數的代碼);否則傳回第二個參數的值。這就是所謂的 邏輯運算符短路求值。

result = true
if result and an_donot_defined_method() then
  print("no erro occured!")
end
--[[
上述代碼輸出的錯誤如下:
stdin:1: attempt to call global 'an_donot_defined_method' (a nil value)
stack traceback:
	stdin:1: in main chunk
	[C]: ?
--]]
result =false 
if (result and an_donot_defined_method())==false  then
  print("no erro occured!")
end
--上述代碼順利通過編譯,即使有一個沒定義的方法,列印結果:no erro occured!
           

一、基本資料類型

          Lua中具有5種基本的資料類型, nil、Boolean、string、Number和table。 在Lua中使用變量不需要提前聲明,變量的類型決定于使用者指派的類型。 可以使用 type()函數判斷變量的類型。其中,nil、Boolean、Number都是用法比較簡單的類型,string、table類型用法稍微複雜點。給一個變量指派為nil,表示釋放該變量。Boolean跟其他語言一樣,隻有true和false兩種值。Number是雙精度浮點數,Lua中沒有整數類型。table類型可以當作數組使用。          在Lua中,變量預設是全局的,這通常導緻一些調試困難,最好盡量顯式的在變量名之前加上 local 關鍵字聲明該變量為局部變量。

gNumber = 10  --這是一個預設全局的變量
print(type(gNumber))
--輸出結果為number
gNumber = nil --之前的number類型gNumber = 10變量被釋放
print(type(gNumber))
--輸出結果為nil

function LocalVarFunction ()
  local pTable = {} --用local關鍵字聲明一個局部變量,這個變量将在執行LocalVarFunction方法後銷毀
     for idx = 1, 5 do 
           local result = true  --這個result将在每個for循環執行之後銷毀
           if result then 
              local pString = "這個字元串将在if代碼塊之後銷毀"
              pTable[idx] = pString
              print(pTable[idx])
           end
     end
end
           

      下面詳細介紹string以及table兩種類型的詳細用法。 1、string類型的用法        Lua中的字元串操作非常出色。下表是一些特殊意義的字元:

特殊的Lua字元串

 字元 意義 字元 意義
     \a 響鈴     \v  垂直制表符 
    \b 倒退     \\ 反斜杠
    \f 換頁符     \“ 雙引号
    \n 換行符     \' 單引号
    \r 換行符    \[ 左方括号
   \t 制表符    \] 右方括号

      a、類型轉換      Lua會根據上下文在合理合法的情況下隐式進行數字和字元之間的轉換。另外,也可以使用 tonumber()函數和tostring()函數顯式地進行字元與數字的轉換。 見代碼執行個體:

--字元與數字的隐式轉換
print("10" + 7)
--輸出結果為:17,将字元10隐私轉化為Number類型計算
print("hello" + 7)
--無法進行運算,即不能隐式将"hello"字元轉化為Number計算
--[[
   系統錯誤如下:
stdin:1: attempt to perform arithmetic on a string value  
stack traceback:  
    stdin:1: in main chunk  
    [C]: ?  
--]]



--字元與數字的顯式轉換
print(tonumber("100")+11)
--輸出結果為:111
print(type(tostring(100)))
--輸出結果為:string
           

       b、常用的字元處理函數介紹    string.char()函數根據傳入的ASCII編碼傳回該編碼對應的字元。如:string.char(10),表示字元換行符,10是換行符的ASCII編碼。         string.len()函數求字元串的長度。如:

print(string.len("hello"))
--輸出結果為:5
           

   string.sub(aString, start, end) 函數傳回指定字元串的子串。如:

gString = "hello Lua"
print(string.sub(gString, 7,9))
--輸出結果為:Lua
           

     string.format()函數格式化輸出指定字元串。 %s表示字元串,%d表示所有數字,%D表示非數字,%a表示字母,%c表示控制字元,%l小寫字母,%p标點符号,%s空格符号,%u大寫字母,%w字母數字,%x十六進制數,%z用0表示的字元。加%字首可以讓特殊符号也能用在格式化中(如:().%+_*?[ ^ $ ]),如%%代表百分比符号。%.4f表示小數點後有4位的浮點數,%02d.表示至少有兩個數字的整數,如果不足兩個數字則用0補足。如:

aString = "哈哈,你是"
bString = "一頭豬"
print(string.format("%s%s", aString, bString))
--輸出結果為:哈哈,你是一頭豬
           

      sting.find(sourceString, targetString) 函數在sourceString字元串中查找第一個符合targetString字元串的位置,如果找到則傳回開始和結束的位置,沒找到則傳回nil。       string.gsub(sourceString, pattern, replacementString) 函數傳回一個字元串,sourceString字元中滿足pattern格式的字元都會被替換成replacementString參數的值。

     string.gfind(sourceString, pattern) 函數周遊一個字元串,一旦查找到符合指定格式的字元串就傳回該子串。

2、table類型的用法

     一般table可以當做數組使用,可以通過table[n]的索引形式通路任意數組中的某個成員。在Lua中,table還能被當做字典dictionary資料使用,并且數組跟字典的用法還能混合使用(實質上還是數組,隻不過索引從數字變成其他屬性值)。       a、使用其他值作為table的索引以及多元table        table還可以使用其他的值作為索引值,并且能用數字跟其他值同時作為同一個table的索引。如:

gTable = {}
gTable.name = "eric"
gTable.gender = "man"
gTable.phonenumber = "0000000000"
gTable[1] = "公司"
gTable[2] = "部門"
for index, value in pairs(gTable) do 
  print(index, value)
end
--[[
輸出結果如下:
1	公司
2	部門
phonenumber	0000000000
gender	man
name	eric
--]]
           

      注意,上述循環中的pairs()函數可以周遊table中的每一對值(索引以及索引對應的value,有點類似字典,不是嗎?)       事實上, table的索引還可以是table本身,這樣就組成了一個多元table或多元字典。跟其他語言的多元數組或字典比起來,使用真是超級友善,非常非常的靈活。如:

gTable = {}
gTable.name = "eric"
gTable.gender = "man"
gTable.phonenumber = "0000000000"
gTable[1] = "公司"
gTable[2] = "部門"
gTable.hobby = {"跑步", "讀書", "遊戲", "動漫"}  -- 多元table,可以通過gTable.hobby[1]的方式通路.即gTable.hobby本身也是一個table
gTable.secTable = {}
gTable.secTable.job = "程式員"
gTable.secTable.label = "寫代碼的"
gTable.secTable.description = "職責是實作産品的邏輯"

for index, value in pairs(gTable) do 
  print(index, value)
  if ("table" == type(value)) then
     for idx, var in pairs(value) do 
         print("二維table:", idx, var)
     end
   end
end
--[[
輸出結果如下:
1	公司
2	部門
hobby	table: 0x7fdceac14bc0
二維table:	1	跑步
二維table:	2	讀書
二維table:	3	遊戲
二維table:	4	動漫
phonenumber	0000000000
gender	man
secTable	table: 0x7fdceac15100
二維table:	label	寫代碼的
二維table:	description	職責是實作産品的邏輯
二維table:	job	程式員
name	eric
--]]
           

      b、table 的常用函數        table.getn()函數,傳回table中元素的個數。如:

gStringTable = {"a", "b","c","d","e"}
for i = 1, table.getn(gStringTable) do
     print(gStringTable[i])
end
           

      table.sort()函數,将table中的元素從小到大排列。如:

gNumberTable = {10, 5, 7, 2,3, 2}
table.sort(gNumberTable)
for i = 1, table.getn(gNumberTable) do 
   print(gNumberTable[i])
end
--輸出結果如下:
2
2
3
5
7
10
           

    table.insert(pTable, position, value) 函數在table中插入一個新值,位置參數如果沒指定,則預設将新值插入到table的末尾。      table.remove(pTable, position) 函數從指定table中删除指定位置的元素并傳回該元素,如果沒有指定删除的位置,則預設删除table的最後一個元素。         介紹到這裡, Lua中基本的資料類型諸位應該都能掌握,休息一下,下面接着開始簡單介紹Lua的基本語句以及函數。

二、Lua中的常用語句結構以及函數

 1、Lua中的常用語句結構介紹

--if 語句結構,如下執行個體:
gTable = {"hello", 10}
if nil ~= gTable[1] and "hello" == gTable[1] then
  print("gTable[1] is" , gStringTable[1])
elseif  10 == gTable[2] then
  print("gTable[2] is", gTable[2])
else 
  print("unkown gTable element")
end
           
--while 和repeat循環語句結構,while先判斷條件,如果true才執行代碼塊(有可能跳過該代碼塊);repeat則是在最後判斷條件,保證代碼塊至少執行一次。
gTable = {1,2,3,4,5,6,7,8,9,10}
index = 1
while gTable[index] < 10 do 
   print("while gTable[",index,"] is ",gTable[index])
   index = index + 1 -- 注意,Lua不支援index++或者index += 1形式的運算符。
end
--[[
while循環輸出結果如下:
while gTable[	1	] is 	1
while gTable[	2	] is 	2
while gTable[	3	] is 	3
while gTable[	4	] is 	4
while gTable[	5	] is 	5
while gTable[	6	] is 	6
while gTable[	7	] is 	7
while gTable[	8	] is 	8
while gTable[	9	] is 	9
--]]

--上一個循環結束後,index = 10
repeat
    print("repeat gTable[",index,"] is ",gTable[index])
   index = index - 2
until index < 1
--[[
輸出結果如下:
repeat gTable[	10	] is 	10
repeat gTable[	8	] is 	8
repeat gTable[	6	] is 	6
repeat gTable[	4	] is 	4
repeat gTable[	2	] is 	2
--]]
           
--for循環結構,for循環結構具有三個參數,初始值,結束值,每個循環增加值。
for index = 1, 5 do --不設定第三個參數的話,預設預設第三個參數是1,即每個循環 index 增加1
   print("for cycle index =",index)
end
--[[
輸出結果為:
for cycle index =	1
for cycle index =	2
for cycle index =	3
for cycle index =	4
for cycle index =	5
--]]

for index = 20 , 0, -5 do --設定第三個參數為-5
 print("for cycle index:",index)
end
--[[
輸出結果:
for cycle index:	20
for cycle index:	15
for cycle index:	10
for cycle index:	5
for cycle index:	0
--]]
           
--break關鍵字可以使循環強制退出,Lua中沒有continue關鍵字,需要通過其他方式實作continue關鍵字,比如if-else語句。或者通過網絡下載下傳Lua的continue關鍵字更新檔安裝來解決該問題

for index = 1, 100, 5 do 
  if index > 10 and index < 25 then  --用if-else語句實作continue關鍵字的功能
     print("continue!!!!! index=",index)
  else 
    if index > 15 and index < 35 then 
       print("break~~~~~index=",index)
       break
    end
    print("At end index=",index)
  end
end

--[[
輸出結果如下:
At end index=	1
At end index=	6
continue!!!!! index=	11
continue!!!!! index=	16
continue!!!!! index=	21
break~~~~~index=	26
--]]
           
--最後還要提的一點是,Lua中switch語句的缺失,用if-elseif-else語句代替的話,顯得非常臃腫,還有其他的一些實作方案。筆者在網上麥子加菲童鞋的部落格中找到一種Lua中代替switch語句非常優雅的方案。下面貼出麥子加菲原代碼:
--Switch語句的替代文法(所有替代方案中覺得最好,最簡潔,最高效,最能展現Lua特點的一種方案)
action = {  
  [1] = function (x) print(x) end,  
  [2] = function (x) print( 2 * x ) end,  
  ["nop"] = function (x) print(math.random()) end,  
  ["my name"] = function (x) print("fred") end,  
}  
 
while true do  
    key = getChar()  
    x = math.ramdon()  
    action[key](x)  
end 
           

2、Lua中的函數      在Lua腳本中, 函數是以function關鍵字開始,然後是函數名稱,參數清單,最後以end關鍵字表示函數結束。需要注意的是, 函數中的參數是局部變量,如果參數清單中存在(...)時,Lua内部将建立一個類型為table的局部變量arg,用來儲存所有調用時傳遞的參數以及參數的個數(arg.n)。

function PrintTable (pTable)
  for index = 1, table.getn(pTable) do 
      print("pTable[",index,"] =",pTable[index])
  end
end

gStringTable = {"hello","how","are","you"}
PrintTable(gStringTable)
--[[
輸出結果為:
pTable[	1	] =	hello
pTable[	2	] =	how
pTable[	3	] =	are
pTable[	4	] =	you
--]]

function PrintFriendInfo (name, gender, ...) 
  local friendInfoString = string.format("name:%s  gender:%d",name,gender)
  if 0 < arg.n then
     for index = 1, arg.n do 
        friendInfoString = string.format("%s otherInfo:%s",friendInfoString, arg[index])
     end
   end
   print(friendInfoString)
end


PrintFriendInfo ("eric", 1, "程式員","2b", 50)

 --輸出結果為:
-- name:eric  gender:1 otherInfo:程式員 otherInfo:2b otherInfo:50
           

       Lua函數的傳回值跟其他語言比較的話,特殊的是能夠傳回多個傳回值。return之後,該Lua函數從Lua的堆棧裡被清理。

function GetUserInfo ()
    local name = "eric"
    local gender = 1
    local hobby = "動漫"
  return name, gender, hobby
end

print(GetUserInfo())

--輸出結果:eric	1	動漫
           

三、Lua中的庫函數

          在本文的最後,介紹一些Lua中常用的庫函數。           1.數學庫           math庫的常用函數: 三角函數math.sin、math.cos、取整函數math.floor、math.ceil、math.max、math.min、随機函數math.random、math.randomseed(os.time())、變量pi和huge。           2、I/O庫          進行I/O操作前,必須先用io.open()函數打開一個檔案。io.open()函數存在兩個參數,一個是要打開的檔案名,另一個是模式字元,類似"r"表示讀取、“w”表示寫入并同時删除檔案原來内容,“a”表示追加,“b”表示打開二進制檔案。該函數會傳回一個表示檔案的傳回值,如果打開出錯則傳回nil,寫入之前需要判斷是否出錯,比如: local file = assert(io.open(filename, “w”))..使用完畢後,調用io.close(file).或file:close()。        幾個常用I/O函數:io.input ()、io.output ()、 io.read()、 io.write()。

local file = assert(io.open(filename, “w”))
if file ~= nil then
  file:write("hello lua!!!!")  --注意,等同于io.write("hello lua!!!!")
  file:close()  --等同于io.close(file)
end
           

         3、調試庫

         debug.getinfo()函數,他的第一個參數 可以是一個函數或一個棧層。傳回結果是一個table,其中包含了函數的定義位置、行号、函數類型、函數名稱等資訊。

         debug.getlocal()函數檢查函數任意局部變量,有兩個參數,第一個是希望查詢的函數棧層,另一個是變量的索引。

         assert(trunk)() 函數,執行參數中代碼塊并在出錯時提供報錯功能。

a = "hello world"
b = "print(a)"
assert(loadstring(b))()
--輸出結果:
hello world
           

        4、幾個處理Lua代碼塊的函數

        loadstring(pString)()函數可以直接執行pString字元串組成的Lua代碼,但不提供報錯功能。

loadstring("for index = 1, 4 do print(\"for cycle index =\",index) end")()
--[[
輸出結果
for cycle index =	1
for cycle index =	2
for cycle index =	3
for cycle index =	4
--]]
           

      dofile(filename) 函數的功能是載入并立刻執行Lua 腳本檔案。可以用來載入定義函數的檔案或者資料檔案、或立即執行的 Lua 代碼。 dofile 函數會将程式的執行目錄作為目前目錄。如果要載入程式執行目錄的子目錄裡的檔案,需要加上子目錄的路徑。

dofile("/Users/ericli/WorkSpace/Lua語言/hellolua.lua")
--輸出結果:Hello Lua!
           

         本篇總結完畢,本篇隻是總結了 Lua 的一些最基本的文法。至于 Lua 的更進階的内容,比如:協同程式、子產品與包、 Lua調用 C 代碼、 C++ 與 Lua 的整合等,還需要在以後的學習過程中深入。

參考資料:

解決Lua文法缺失及替代措施

書籍:《Lua程式設計》、《Lua遊戲開發實踐指南》

繼續閱讀