Lua 是一種輕量小巧的腳本語言,用標準C語言編寫並以源代碼形式開放, 其設計目的是爲了嵌入應用程序中,從而爲應用程序提供靈活的擴展和定製功能。php
Lua 是巴西里約熱內盧天主教大學(Pontifical Catholic University of Rio de Janeiro)裏的一個研究小組,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所組成並於1993年開發。html
其設計目的是爲了嵌入應用程序中,從而爲應用程序提供靈活的擴展和定製功能。前端
輕量級: 它用標準C語言編寫並以源代碼形式開放,編譯後僅僅一百餘K,能夠很方便的嵌入別的程序裏。java
可擴展: Lua提供了很是易於使用的擴展接口和機制:由宿主語言(一般是C或C++)提供這些功能,Lua能夠使用它們,就像是原本就內置的功能同樣。mysql
其它特性linux
:ios
接下來咱們使用 Lua 來輸出"Hello World!"c++
print("Hello World!")git
嘗試一下 »程序員
運行後,會在屏幕上顯示 Hello, world!。
由 sf0501 建立,Loen 最後一次修改 2015-09-08
Linux & Mac上安裝 Lua 安裝很是簡單,只須要下載源碼包並在終端解壓編譯便可,本文使用了5.3.0版本進行安裝:
curl -R -O http://www.lua.org/ftp/lua-5.3.0.tar.gz tar zxf lua-5.3.0.tar.gz cd lua-5.3.0 make linux test make install
curl -R -O http://www.lua.org/ftp/lua-5.3.0.tar.gz tar zxf lua-5.3.0.tar.gz cd lua-5.3.0 make macosx test make install
接下來咱們建立一個 helloWorld.lua:
print("Hello World!")
執行如下命令:
$ lua helloWorld
輸出結果爲:
Hello World!
window下你能夠使用一個叫"SciTE"的IDE環境來執行lua程序,下載地址爲:
雙擊安裝後便可在該環境下編寫 Lua 程序並運行。
你也能夠使用 Lua 官方推薦的方法使用 LuaDist:http://luadist.org/
若是安裝的時候報錯: lua.c:80:31: fatal error: readline/readline.h: No such file or directory
解決方法: 缺乏libreadline-dev依賴包
centos 系統: yum install readline-devel
debian 系統: apt-get install libreadline-dev
由 sf0501 建立,youj 最後一次修改 2015-09-06
Lua 學習起來很是簡單,咱們能夠建立第一個 Lua 程序!
Lua 提供了交互式編程模式。咱們能夠在命令行中輸入程序並當即查看效果。
Lua 交互式編程模式能夠經過命令 lua -i 或 lua 來啓用:
$ lua -i $ Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio >
在命令行中,輸入如下命令:
> print("Hello World!")
接着咱們按下回車鍵,輸出結果以下:
> print("Hello World!") Hello World! >
咱們能夠將 Lua 程序代碼保持到一個以 lua 結尾的文件,並執行,該模式稱爲腳本式編程,如咱們將以下代碼存儲在名爲 hello.lua 的腳本文件中:
print("Hello World!") print("www.w3cschool.cn")
使用 lua 名執行以上腳本,輸出結果爲:
$ lua test.lua Hello World! www.w3cschool.cn
咱們也能夠將代碼修改成以下形式來執行腳本(在開頭添加:#!/usr/local/bin/lua):
#!/usr/local/bin/lua print("Hello World!") print("www.w3cschool.cn")
以上代碼中,咱們指定了 Lua 的解釋器 /usr/local/bin directory。加上 # 號標記解釋器會忽略它。接下來咱們爲腳本添加可執行權限,並執行:
./test.lua Hello World! www.w3cschool.cn
兩個減號是單行註釋:
--
--[[ 多行註釋 多行註釋 --]]
Lua 標示符用於定義一個變量,函數獲取其餘用戶定義的項。標示符以一個字母 A 到 Z 或 a 到 z 或下劃線 _ 開頭後加上0個或多個字母,下劃線,數字(0到9)。
最好不要使用下劃線加大寫字母的標示符,由於Lua的保留字也是這樣的。
Lua 不容許使用特殊字符如 @, $, 和 % 來定義標示符。 Lua 是一個區分大小寫的編程語言。所以在 Lua 中 W3c 與 w3c 是兩個不一樣的標示符。如下列出了一些正確的標示符:
mohd zara abc move_name a_123 myname50 _temp j a23b9 retVal
如下列出了 Lua 的保留關鍵字。保留關鍵字不能做爲常量或變量或其餘用戶自定義標示符:
and | break | do | else |
---|---|---|---|
elseif | end | false | for |
function | if | in | local |
nil | not | or | repeat |
return | then | true | until |
while |
通常約定,如下劃線開頭鏈接一串大寫字母的名字(好比 _VERSION)被保留用於 Lua 內部全局變量。
在默認狀況下,變量老是認爲是全局的。
全局變量不須要聲明,給一個變量賦值後即建立了這個全局變量,訪問一個沒有初始化的全局變量也不會出錯,只不過獲得的結果是:nil。
> print(b) nil > b=10 > print(b) 10 >
若是你想刪除一個全局變量,只須要將變量賦值爲nil。
b = nil print(b) --> nil
這樣變量b就好像從沒被使用過同樣。換句話說, 當且僅當一個變量不等於nil時,這個變量即存在。
由 sf0501 建立,youj 最後一次修改 2015-09-10
Lua是動態類型語言,變量不要類型定義,只須要爲變量賦值。 值能夠存儲在變量中,做爲參數傳遞或結果返回。
Lua中有8個基本類型分別爲:nil、boolean、number、string、userdata、function、thread和table。
數據類型 | 描述 |
---|---|
nil | 這個最簡單,只有值nil屬於該類,表示一個無效值(在條件表達式中至關於false)。 |
boolean | 包含兩個值:false和true。 |
number | 表示雙精度類型的實浮點數 |
string | 字符串由一對雙引號或單引號來表示 |
function | 由 C 或 Lua 編寫的函數 |
userdata | 表示任意存儲在變量中的C數據結構 |
thread | 表示執行的獨立線路,用於執行協同程序 |
table | Lua 中的表(table)實際上是一個"關聯數組"(associative arrays),數組的索引能夠是數字或者是字符串。在 Lua 裏,table 的建立是經過"構造表達式"來完成,最簡單構造表達式是{},用來建立一個空表。 |
咱們能夠使用type函數測試給定變量或者值的類型:
print(type("Hello world")) --> string print(type(10.4*3)) --> number print(type(print)) --> function print(type(type)) --> function print(type(true)) --> boolean print(type(nil)) --> nil print(type(type(X))) --> string
nil 類型表示一種沒有任何有效值,它只有一個值 -- nil,例如打印一個沒有賦值的變量,便會輸出一個 nil 值:
> print(type(a)) nil >
對於全局變量和 table,nil 還有一個"刪除"做用,給全局變量或者 table 表裏的變量賦一個 nil 值,等同於把它們刪掉,執行下面代碼就知:
tab1 = { key1 = "val1", key2 = "val2", "val3" } for k, v in pairs(tab1) do print(k .. " - " .. v) end tab1.key1 = nil for k, v in pairs(tab1) do print(k .. " - " .. v) end
boolean 類型只有兩個可選值:true(真) 和 false(假),Lua 把 false 和 nil 看做是"假",其餘的都爲"真":
print(type(true)) print(type(false)) print(type(nil)) if type(false) or type(nil) then print("false and nil are false!") else print("other is true!") end
以上代碼執行結果以下:
$ lua test.lua boolean boolean nil false and nil are false!
Lua 默認只有一種 number 類型 -- double(雙精度)類型(默認類型能夠修改 luaconf.h 裏的定義),如下幾種寫法都被看做是 number 類型:
print(type(2)) print(type(2.2)) print(type(0.2)) print(type(2e+1)) print(type(0.2e-1)) print(type(7.8263692594256e-06))
以上代碼執行結果:
number number number number number number
字符串由一對雙引號或單引號來表示。
string1 = "this is string1" string2 = 'this is string2'
也能夠用 2 個方括號 "[[]]" 來表示"一塊"字符串。
html = [[ <html> <head></head> <body> <a href="//www.w3cschool.cn/">w3cschoolW3Cschool教程</a> </body> </html> ]] print(html)
如下代碼執行結果爲:
<html> <head></head> <body> <a href="//www.w3cschool.cn/">w3cschoolW3Cschool教程</a> </body> </html>
在對一個數字字符串上進行算術操做時,Lua 會嘗試將這個數字字符串轉成一個數字:
> print("2" + 6) 8.0 > print("2" + "6") 8.0 > print("2 + 6") 2 + 6 > print("-2e2" * "6") -1200.0 > print("error" + 1) stdin:1: attempt to perform arithmetic on a string value stack traceback: stdin:1: in main chunk [C]: in ? >
以上代碼中"error" + 1執行報錯了,字符串鏈接使用的是 .. ,如:
> print("a" .. 'b') ab > print(157 .. 428) 157428 >
使用 # 來計算字符串的長度,放在字符串前面,以下實例:
> len = "www.w3cschool.cn" > print(#len) 16 > print(#"www.w3cschool.cn") 16 >
在 Lua 裏,table 的建立是經過"構造表達式"來完成,最簡單構造表達式是{},用來建立一個空表。也能夠在表裏添加一些數據,直接初始化表:
-- 建立一個空的 table local tbl1 = {} -- 直接初始表 local tbl2 = {"apple", "pear", "orange", "grape"}
Lua 中的表(table)實際上是一個"關聯數組"(associative arrays),數組的索引能夠是數字或者是字符串。
注意:Lua 中的表(table)只能遍歷打印,直接打印會打印出table加一串數字
-- table_test.lua 腳本文件 a = {} a["key"] = "value" key = 10 a[key] = 22 a[key] = a[key] + 11 for k, v in pairs(a) do print(k .. " : " .. v) end
腳本執行結果爲:
$ lua table_test.lua key : value 10 : 33
不一樣於其餘語言的數組把 0 做爲數組的初始索引,在 Lua 裏表的默認初始索引通常以 1 開始。
-- table_test2.lua 腳本文件 local tbl = {"apple", "pear", "orange", "grape"} for key, val in pairs(tbl) do print("Key", key) end
腳本執行結果爲:
$ lua table_test2.lua Key 1 Key 2 Key 3 Key 4
table 不會固定長度大小,有新數據添加時 table 長度會自動增加,沒初始的 table 都是 nil。
-- table_test3.lua 腳本文件 a3 = {} for i = 1, 10 do a3[i] = i end a3["key"] = "val" print(a3["key"]) print(a3["none"])
腳本執行結果爲:
$ lua table_test3.lua val nil
在 Lua 中,函數是被看做是"第一類值(First-Class Value)",函數能夠存在變量裏:
-- function_test.lua 腳本文件 function factorial1(n) if n == 0 then return 1 else return n * factorial1(n - 1) end end print(factorial1(5)) factorial2 = factorial1 print(factorial2(5))
腳本執行結果爲:
$ lua function_test.lua 120 120
function 能夠以匿名函數(anonymous function)的方式經過參數傳遞:
-- function_test2.lua 腳本文件 function anonymous(tab, fun) for k, v in pairs(tab) do print(fun(k, v)) end end tab = { key1 = "val1", key2 = "val2" } anonymous(tab, function(key, val) return key .. " = " .. val end)
腳本執行結果爲:
$ lua function_test2.lua key1 = val1 key2 = val2
在 Lua 裏,最主要的線程是協同程序(coroutine)。它跟線程(thread)差很少,擁有本身獨立的棧、局部變量和指令指針,能夠跟其餘協同程序共享全局變量和其餘大部分東西。
線程跟協程的區別:線程能夠同時多個運行,而協程任意時刻只能運行一個,而且處於運行狀態的協程只有被掛起(suspend)時纔會暫停。
userdata 是一種用戶自定義數據,用於表示一種由應用程序或 C/C++ 語言庫所建立的類型,能夠將任意 C/C++ 的任意數據類型的數據(一般是 struct 和 指針)存儲到 Lua 變量中調用。
由 sf0501 建立,youj 最後一次修改 2015-09-27
變量在使用前,必須在代碼中進行聲明,即建立該變量。編譯程序執行代碼以前編譯器須要知道如何給語句變量開闢存儲區,用於存儲變量的值。
Lua 變量有三種類型:全局變量、局部變量、表中的域。
函數外的變量默認爲全局變量,除非用 local 顯示聲明。函數內變量與函數的參數默認爲局部變量。
局部變量的做用域爲從聲明位置開始到所在語句塊結束(或者是直到下一個同名局部變量的聲明)。
變量的默認值均爲 nil。
若是沒有用local定義,即便在函數內部定義的變量也是全局變量!
-- test.lua 文件腳本 a = 5 -- 全局變量 local b = 5 -- 局部變量 function joke() c = 5 -- 全局變量 local d = 6 -- 局部變量 end joke() print(c,d) --> 5 nil do local a = 6 -- 局部變量 b = 6 -- 全局變量 print(a,b); --> 6 6 end print(a,b) --> 5 6
執行以上實例輸出結果爲:
$ lua test.lua 5 nil 6 6 5 6
賦值是改變一個變量的值和改變表域的最基本的方法。
a = "hello" .. "world" t.n = t.n + 1
Lua能夠對多個變量同時賦值,變量列表和值列表的各個元素用逗號分開,賦值語句右邊的值會依次賦給左邊的變量。
a, b = 10, 2*x <--> a=10; b=2*x
遇到賦值語句Lua會先計算右邊全部的值而後再執行賦值操做,因此咱們能夠這樣進行交換變量的值:
x, y = y, x -- swap 'x' for 'y' a[i], a[j] = a[j], a[i] -- swap 'a[i]' for 'a[i]'
當變量個數和值的個數不一致時,Lua會一直以變量個數爲基礎採起如下策略:
a. 變量個數 > 值的個數 按變量個數補足nil b. 變量個數 < 值的個數 多餘的值會被忽略
例如:
a, b, c = 0, 1 print(a,b,c) --> 0 1 nil a, b = a+1, b+1, b+2 -- value of b+2 is ignored print(a,b) --> 1 2 a, b, c = 0 print(a,b,c) --> 0 nil nil
上面最後一個例子是一個常見的錯誤狀況,注意:若是要對多個變量賦值必須依次對每一個變量賦值。
a, b, c = 0, 0, 0 print(a,b,c) --> 0 0 0
多值賦值常常用來交換變量,或將函數調用返回給變量:
a, b = f()
f()返回兩個值,第一個賦給a,第二個賦給b。
應該儘量的使用局部變量,有兩個好處:
對 table 的索引使用方括號 []。Lua 也提供了 . 操做。
t[i] t.i -- 當索引爲字符串類型時的一種簡化寫法 gettable_event(t,i) -- 採用索引訪問本質上是一個相似這樣的函數調用
例如:
> site = {} > site["key"] = "www.w3cschool.cn" > print(site["key"]) www.w3cschool.cn > print(site.key) www.w3cschool.cn
由 sf0501 建立,youj 最後一次修改 2015-09-05
不少狀況下咱們須要作一些有規律性的重複操做,所以在程序中就須要重複執行某些語句。
一組被重複執行的語句稱之爲循環體,可否繼續重複,決定循環的終止條件。
循環結構是在必定條件下反覆執行某段程序的流程結構,被反覆執行的程序被稱爲循環體。
循環語句是由循環體及循環的終止條件兩部分組成的。
Lua 語言提供瞭如下幾種循環處理方式:
循環類型 | 描述 |
---|---|
while 循環 | 在條件爲 true 時,讓程序重複地執行某些語句。執行語句前會先檢查條件是否爲 true。 |
for 循環 | 重複執行指定語句,重複次數可在 for 語句中控制。 |
Lua repeat...until | 重複執行循環,直到 指定的條件爲真時爲止 |
循環嵌套 | 能夠在循環內嵌套一個或多個循環語句(while、for、do..while) |
循環控制語句用於控制程序的流程, 以實現程序的各類結構方式。
Lua 支持如下循環控制語句:
控制語句 | 描述 |
---|---|
break 語句 | 退出當前循環或語句,並開始腳本執行緊接着的語句。 |
在循環體中若是條件永遠爲 true 循環語句就會永遠執行下去,如下以 while 循環爲例:
while( true ) do print("循環將永遠執行下去") end
Lua 編程語言中 while 循環語句在判斷條件爲 true 時會重複執行循環體語句。
Lua 編程語言中 while 循環語法:
while(condition) do statements end
statements(循環體語句) 能夠是一條或多條語句,condition(條件) 能夠是任意表達式,在 condition(條件) 爲 true 時執行循環體語句。
流程圖以下:
在以上流程圖中咱們能夠看出在condition(條件)爲 false 時會跳過當前循環並開始腳本執行緊接着的語句。
如下實例循環輸出 a 的值:
a=10 while( a < 20 ) do print("a 的值爲:", a) a = a+1 end
執行以上代碼,輸出結果以下:
a 的值爲: 10 a 的值爲: 11 a 的值爲: 12 a 的值爲: 13 a 的值爲: 14 a 的值爲: 15 a 的值爲: 16 a 的值爲: 17 a 的值爲: 18 a 的值爲: 19
Lua 編程語言中 for 循環語句能夠重複執行指定語句,重複次數可在 for 語句中控制。
Lua 編程語言中 for語句有兩大類::
Lua 編程語言中數值for循環語法格式:
for var=exp1,exp2,exp3 do <執行體> end
var從exp1變化到exp2,每次變化以exp3爲步長遞增var,並執行一次"執行體"。exp3是可選的,若是不指定,默認爲1。
for i=1,f(x) do print(i) end for i=10,1,-1 do print(i) end
for的三個表達式在循環開始前一次性求值,之後再也不進行求值。好比上面的f(x)只會在循環開始前執行一次,其結果用在後面的循環中。
驗證以下:
#!/usr/local/bin/lua function f(x) print("function") return x*2 end for i=1,f(5) do print(i) end
以上實例輸出結果爲:
function 1 2 3 4 5 6 7 8 9 10
能夠看到 函數f(x)只在循環開始前執行一次。
泛型for循環經過一個迭代器函數來遍歷全部值,相似java中的foreach語句。
Lua 編程語言中泛型for循環語法格式:
--打印數組a的全部值 for i,v in ipairs(a) do print(v) end
i是數組索引值,v是對應索引的數組元素值。ipairs是Lua提供的一個迭代器函數,用來迭代數組。
循環數組 days:
#!/usr/local/bin/lua days = {"Suanday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"} for i,v in ipairs(days) do print(v) end
以上實例輸出結果爲:
Suanday Monday Tuesday Wednesday Thursday Friday Saturday
Lua 編程語言中 repeat...until 循環語句不一樣於 for 和 while循環,for 和 while循環d的條件語句在當前循環執行開始時判斷,而 repeat...until 循環的條件語句在當前循環結束後判斷。
Lua 編程語言中 repeat...until 循環語法格式:
repeat statements while( condition )
repeat...until 是條件後行,因此repeat...until 的循環體裏面至少要運行一次。
statements(循環體語句) 能夠是一條或多條語句,condition(條件) 能夠是任意表達式,在 condition(條件) 爲 true 時執行循環體語句。
在condition(條件)爲 false 時會跳過當前循環並開始腳本執行緊接着的語句。
Lua repeat...until 循環流程圖以下:
--[ 變量定義 --] a = 10 --[ 執行循環 --] repeat print("a的值爲:", a) a = a + 1 until( a > 15 )
執行以上代碼,程序輸出結果爲:
a的值爲: 10 a的值爲: 11 a的值爲: 12 a的值爲: 13 a的值爲: 14 a的值爲: 15
Lua 編程語言中容許循環中嵌入循環。如下實例演示了 Lua 循環嵌套的應用。
Lua 編程語言中 for 循環嵌套語法格式:
for init,max/min value, increment do for init,max/min value, increment do statements end statements end
Lua 編程語言中 while 循環嵌套語法格式:
while(condition) do while(condition) do statements end statements end
Lua 編程語言中 repeat...until 循環嵌套語法格式:
repeat statements repeat statements until( condition ) until( condition )
除了以上同類型循環嵌套外,咱們還能夠使用不一樣的循環類型來嵌套,如 for 循環體中嵌套 while 循環。
如下實例使用了for循環嵌套:
j =2 for i=2,10 do for j=2,(i/j) , 2 do if(not(i%j)) then break end if(j > (i/j))then print("i 的值爲:",i) end end end
以上代碼執行結果爲:
i 的值爲: 8 i 的值爲: 9 i 的值爲: 10
Lua 編程語言 break 語句插入在循環體中,用於退出當前循環或語句,並開始腳本執行緊接着的語句。
若是你使用循環嵌套,break語句將中止最內層循環的執行,並開始執行的外層的循環語句。
Lua 編程語言中 break 語句語法格式:
break
流程圖:
如下實例執行 while 循環,在變量 a 小於 20 時輸出 a 的值,並在 a 大於 15 時終止執行循環:
--[ 定義變量 --] a = 10 --[ while 循環 --] while( a < 20 ) do print("a 的值爲:", a) a=a+1 if( a > 15) then --[ 使用 break 語句終止循環 --] break end end
以上代碼執行結果以下:
a 的值爲: 10 a 的值爲: 11 a 的值爲: 12 a 的值爲: 13 a 的值爲: 14 a 的值爲: 15
由 sf0501 建立,youj 最後一次修改 2015-09-05
Lua 編程語言流程控制語句經過程序設定一個或多個條件語句來設定。在條件爲 true 時執行指定程序代碼,在條件爲 false 時執行其餘指定代碼。
如下是典型的流程控制流程圖:
控制結構的條件表達式結果能夠是任何值,Lua認爲false和nil爲假,true 和非nil爲真。
要注意的是Lua中 0 爲 true:
--[ 0 爲true ] if(0) then print("0 爲真") end
以上代碼輸出結果爲:
0 爲真
Lua 提供瞭如下控制結構語句:
語句 | 描述 |
---|---|
if 語句 | if 語句 由一個布爾表達式做爲條件判斷,其後緊跟其餘語句組成。 |
if...else 語句 | if 語句 能夠與 else 語句搭配使用, 在 if 條件表達式爲 false 時執行 else 語句代碼。 |
if 嵌套語句 | 你能夠在if 或 else if中使用一個或多個 if 或 else if 語句 。 |
Lua if 語句 由一個布爾表達式做爲條件判斷,其後緊跟其餘語句組成。
if(布爾表達式) then --[ 在布爾表達式爲 true 時執行的語句 --] end
在布爾表達式爲 true 時會if中的代碼塊會被執行,在布爾表達式爲 false 時,緊跟在 if 語句 end 以後的代碼會被執行。 Lua認爲false和nil爲假,true 和非nil爲真。要注意的是Lua中 0 爲 true。 if 語句流程圖以下: 如下實例用於判斷變量 a 的值是否小於 20:
--[ 定義變量 --] a = 10; --[ 使用 if 語句 --] if( a < 20 ) then --[ if 條件爲 true 時打印如下信息 --] print("a 小於 20" ); end print("a 的值爲:", a);
以上代碼執行結果以下:
a 小於 20 a 的值爲: 10
Lua if 語句能夠與 else 語句搭配使用, 在 if 條件表達式爲 false 時執行 else 語句代碼塊。
Lua if...else 語句語法格式以下:
if(布爾表達式) then --[ 布爾表達式爲 true 時執行該語句塊 --] else --[ 布爾表達式爲 false 時執行該語句塊 --] end
在布爾表達式爲 true 時會if中的代碼塊會被執行,在布爾表達式爲 false 時,else 的代碼塊會被執行。
Lua認爲false和nil爲假,true 和非nil爲真。要注意的是Lua中 0 爲 true。
if 語句流程圖以下:
如下實例用於判斷變量 a 的值:
--[ 定義變量 --] a = 100; --[ 檢查條件 --] if( a < 20 ) then --[ if 條件爲 true 時執行該語句塊 --] print("a 小於 20" ) else --[ if 條件爲 false 時執行該語句塊 --] print("a 大於 20" ) end print("a 的值爲 :", a)
以上代碼執行結果以下:
a 大於 20 a 的值爲 : 100
Lua if 語句能夠與 else if...else 語句搭配使用, 在 if 條件表達式爲 false 時執行 else if...else 語句代碼塊,用於檢測多個條件語句。
Lua if...else if...else 語句語法格式以下:
if( 布爾表達式 1) then --[ 在布爾表達式 1 爲 true 時執行該語句塊 --] else if( 布爾表達式 2) --[ 在布爾表達式 2 爲 true 時執行該語句塊 --] else if( 布爾表達式 3) --[ 在布爾表達式 3 爲 true 時執行該語句塊 --] else --[ 若是以上布爾表達式都不爲 true 則執行該語句塊 --] end
如下實例對變量 a 的值進行判斷:
--[ 定義變量 --] a = 100 --[ 檢查布爾條件 --] if( a == 10 ) then --[ 若是條件爲 true 打印如下信息 --] print("a 的值爲 10" ) elseif( a == 20 ) then --[ if else if 條件爲 true 時打印如下信息 --] print("a 的值爲 20" ) elseif( a == 30 ) then --[ if else if condition 條件爲 true 時打印如下信息 --] print("a 的值爲 30" ) else --[ 以上條件語句沒有一個爲 true 時打印如下信息 --] print("沒有匹配 a 的值" ) end print("a 的真實值爲: ", a )
以上代碼執行結果以下:
沒有匹配 a 的值 a 的真實值爲: 100
Lua if 語句容許嵌套, 這就意味着你能夠在一個 if 或 else if 語句中插入其餘的 if 或 else if 語句。
if( 布爾表達式 1) then --[ 布爾表達式 1 爲 true 時執行該語句塊 --] if(布爾表達式 2) then --[ 布爾表達式 2 爲 true 時執行該語句塊 --] end end
你能夠用一樣的方式嵌套 else if...else 語句。如下實例用於判斷變量 a 和 b 的值:
--[ 定義變量 --] a = 100; b = 200; --[ 檢查條件 --] if( a == 100 ) then --[ if 條件爲 true 時執行如下 if 條件判斷 --] if( b == 200 ) then --[ if 條件爲 true 時執行該語句塊 --] print("a 的值爲 100 b 的值爲 200" ); end end print("a 的值爲 :", a ); print("b 的值爲 :", b );
以上代碼執行結果以下:
a 的值爲 100 b 的值爲 200 a 的值爲 : 100 b 的值爲 : 200
由 sf0501 建立,陳 最後一次修改 2015-09-28
在Lua中,函數是對語句和表達式進行抽象的主要方法。既能夠用來處理一些特殊的工做,也能夠用來計算一些值。
Lua 提供了許多的內建函數,你能夠很方便的在程序中調用它們,如print()函數能夠將傳入的參數打印在控制檯上。
Lua 函數主要有兩種用途:
Lua 編程語言函數定義格式以下:
optional_function_scope function function_name( argument1, argument2, argument3..., argumentn) function_body return result_params_comma_separated end
解析:
: 該參數是可選的指定函數是全局函數仍是局部函數,未設置該參數默認爲全局函數,若是你須要設置函數爲局部函數須要使用關鍵字
local。
指定函數名稱。
函數參數,多個參數以逗號隔開,函數也能夠不帶參數。
函數體,函數中須要執行的代碼語句塊。
函數返回值,Lua語言函數能夠返回多個值,每一個值以逗號隔開。
如下實例定義了函數 max(),參數爲 num1, num2,用於比較兩值的大小,並返回最大值:
--[[ 函數返回兩個值的最大值 --]] function max(num1, num2) if (num1 > num2) then result = num1; else result = num2; end return result; end -- 調用函數 print("兩值比較最大值爲 ",max(10,4)) print("兩值比較最大值爲 ",max(5,6))
以上代碼執行結果爲:
兩值比較最大值爲 10 兩值比較最大值爲 6
Lua 中咱們能夠將函數做爲參數傳遞給函數,以下實例:
myprint = function(param) print("這是打印函數 - ##",param,"##") end function add(num1,num2,functionPrint) result = num1 + num2 -- 調用傳遞的函數參數 functionPrint(result) end myprint(10) -- myprint 函數做爲參數傳遞 add(2,5,myprint)
以上代碼執行結果爲:
這是打印函數 - ## 10 ## 這是打印函數 - ## 7 ##
Lua函數能夠返回多個結果值,好比string.find,其返回匹配串"開始和結束的下標"(若是不存在匹配串返回nil)。
> s, e = string.find("www.w3cschool.cn", "w3cschool") > print(s, e) 5 13
Lua函數中,在return後列出要返回的值得列表便可返回多值,如:
function maximum (a) local mi = 1 -- 最大值索引 local m = a[mi] -- 最大值 for i,val in ipairs(a) do if val > m then mi = i m = val end end return m, mi end print(maximum({8,10,23,12,5}))
以上代碼執行結果爲:
23 3
Lua函數能夠接受可變數目的參數,和C語言相似在函數參數列表中使用三點(...) 表示函數有可變的參數。
Lua將函數的參數放在一個叫arg的表中,#arg 表示傳入參數的個數。
例如,咱們計算幾個數的平均值:
function average(...) result = 0 local arg={...} for i,v in ipairs(arg) do result = result + v end print("總共傳入 " .. #arg .. " 個數") return result/#arg end print("平均值爲",average(10,5,3,4,5,6))
以上代碼執行結果爲:
總共傳入 6 個數 平均值爲 5.5
由 sf0501 建立,youj 最後一次修改 2015-09-22
運算符是一個特殊的符號,用於告訴解釋器執行特定的數學或邏輯運算。Lua提供瞭如下幾種運算符類型:
下表列出了 Lua 語言中的經常使用算術運算符,設定 A 的值爲10,B 的值爲 20:
操做符 | 描述 | 實例 |
---|---|---|
+ | 加法 | A + B 輸出結果 30 |
- | 減法 | A - B 輸出結果 -10 |
* | 乘法 | A * B 輸出結果 200 |
/ | 除法 | B / A w輸出結果 2 |
% | 取餘 | B % A 輸出結果 0 |
^ | 乘冪 | A^2 輸出結果 100 |
- | 負號 | -A 輸出結果v -10 |
咱們能夠經過如下實例來更加透徹的理解算術運算符的應用:
a = 21 b = 10 c = a + b print("Line 1 - c 的值爲 ", c ) c = a - b print("Line 2 - c 的值爲 ", c ) c = a * b print("Line 3 - c 的值爲 ", c ) c = a / b print("Line 4 - c 的值爲 ", c ) c = a % b print("Line 5 - c 的值爲 ", c ) c = a^2 print("Line 6 - c 的值爲 ", c ) c = -a print("Line 7 - c 的值爲 ", c )
以上程序執行結果爲:
Line 1 - c 的值爲 31 Line 2 - c 的值爲 11 Line 3 - c 的值爲 210 Line 4 - c 的值爲 2.1 Line 5 - c 的值爲 1 Line 6 - c 的值爲 441 Line 7 - c 的值爲 -21
下表列出了 Lua 語言中的經常使用關係運算符,設定 A 的值爲10,B 的值爲 20:
操做符 | 描述 | 實例 |
---|---|---|
== | 等於,檢測兩個值是否相等,相等返回 true,不然返回 false | (A == B) 爲 false。 |
~= | 不等於,檢測兩個值是否相等,相等返回 false,不然返回 true< | (A ~= B) 爲 true。 |
> | 大於,若是左邊的值大於右邊的值,返回 true,不然返回 false | (A > B) 爲 false。 |
< | 小於,若是左邊的值大於右邊的值,返回 false,不然返回 true | (A < B) 爲 true。 |
>= | 大於等於,若是左邊的值大於等於右邊的值,返回 true,不然返回 false | (A >= B) is not true. |
<= | 小於等於, 若是左邊的值小於等於右邊的值,返回 true,不然返回 false | (A <= B) is true. |
咱們能夠經過如下實例來更加透徹的理解關係運算符的應用:
a = 21 b = 10 if( a == b ) then print("Line 1 - a 等於 b" ) else print("Line 1 - a 不等於 b" ) end if( a ~= b ) then print("Line 2 - a 不等於 b" ) else print("Line 2 - a 等於 b" ) end if ( a < b ) then print("Line 3 - a 小於 b" ) else print("Line 3 - a 大於等於 b" ) end if ( a > b ) then print("Line 4 - a 大於 b" ) else print("Line 5 - a 小於等於 b" ) end -- 修改 a 和 b 的值 a = 5 b = 20 if ( a <= b ) then print("Line 5 - a 小於等於 b" ) end if ( b >= a ) then print("Line 6 - b 大於等於 a" ) end
以上程序執行結果爲:
Line 1 - a 不等於 b Line 2 - a 不等於 b Line 3 - a 大於等於 b Line 4 - a 大於 b Line 5 - a 小於等於 b Line 6 - b 大於等於 a
下表列出了 Lua 語言中的經常使用邏輯運算符,設定 A 的值爲 true,B 的值爲 false:
操做符 | 描述 | 實例 |
---|---|---|
and | 邏輯與操做符。 若是兩邊的操做都爲 true 則條件爲 true。 | (A and B) 爲 false。 |
or | 邏輯或操做符。 若是兩邊的操做任一一個爲 true 則條件爲 true。 | (A or B) 爲 true。 |
not | 邏輯非操做符。與邏輯運算結果相反,若是條件爲 true,邏輯非爲 false。 | not(A and B) 爲 true。 |
實例
咱們能夠經過如下實例來更加透徹的理解邏輯運算符的應用:
a = true b = true if ( a and b ) then print("a and b - 條件爲 true" ) end if ( a or b ) then print("a or b - 條件爲 true" ) end print("---------分割線---------" ) -- 修改 a 和 b 的值 a = false b = true if ( a and b ) then print("a and b - 條件爲 true" ) else print("a and b - 條件爲 false" ) end if ( not( a and b) ) then print("not( a and b) - 條件爲 true" ) else print("not( a and b) - 條件爲 false" ) end
以上程序執行結果爲:
a and b - 條件爲 true a or b - 條件爲 true ---------分割線--------- a and b - 條件爲 false not( a and b) - 條件爲 true
下表列出了 Lua 語言中的鏈接運算符與計算表或字符串長度的運算符:
操做符 | 描述 | 實例 |
---|---|---|
.. | 鏈接兩個字符串 | a..b ,其中 a 爲 "Hello " , b 爲 "World", 輸出結果爲 "Hello World"。 |
# | 一元運算符,返回字符串或表的長度。 | #"Hello" 返回 5 |
咱們能夠經過如下實例來更加透徹的理解鏈接運算符與計算表或字符串長度的運算符的應用:
a = "Hello " b = "World" print("鏈接字符串 a 和 b ", a..b ) print("b 字符串長度 ",#b ) print("字符串 Test 長度 ",#"Test" ) print("w3cschool在線教程網址長度 ",#"www.w3cschool.cn" )
以上程序執行結果爲:
鏈接字符串 a 和 b Hello World b 字符串長度 5 字符串 Test 長度 4 w3cschool在線教程網址長度 16
從高到低的順序:
^ not - (unary) * / + - .. < > <= >= ~= == and or
除了^和..外全部的二元運算符都是左鏈接的。
a+i < b/2+1 <--> (a+i) < ((b/2)+1) 5+x^2*8 <--> 5+((x^2)*8) a < y and y <= z <--> (a < y) and (y <= z) -x^2 <--> -(x^2) x^y^z <--> x^(y^z)
咱們能夠經過如下實例來更加透徹的瞭解 Lua 語言運算符的優先級:
a = 20 b = 10 c = 15 d = 5 e = (a + b) * c / d;-- ( 30 * 15 ) / 5 print("(a + b) * c / d 運算值爲 :",e ) e = ((a + b) * c) / d; -- (30 * 15 ) / 5 print("((a + b) * c) / d 運算值爲 :",e ) e = (a + b) * (c / d);-- (30) * (15/5) print("(a + b) * (c / d) 運算值爲 :",e ) e = a + (b * c) / d; -- 20 + (150/5) print("a + (b * c) / d 運算值爲 :",e )
以上程序執行結果爲:
(a + b) * c / d 運算值爲 : 90.0 ((a + b) * c) / d 運算值爲 : 90.0 (a + b) * (c / d) 運算值爲 : 90.0 a + (b * c) / d 運算值爲 : 50.0
由 sf0501 建立,youj 最後一次修改 2015-09-26
字符串或串(String)是由數字、字母、下劃線組成的一串字符。
Lua 語言中字符串能夠使用如下三種方式來表示:
以上三種方式的字符串實例以下:
string1 = "Lua" print("\"字符串 1 是\"",string1) string2 = 'w3cschool.cn' print("字符串 2 是",string2) string3 = [["Lua 教程"]] print("字符串 3 是",string3)
以上代碼執行輸出結果爲:
"字符串 1 是" Lua 字符串 2 是 w3cschool.cn 字符串 3 是 "Lua 教程"
轉義字符用於表示不能直接顯示的字符,好比後退鍵,回車鍵,等。如在字符串轉換雙引號能夠使用 """。
全部的轉義字符和所對應的意義:
轉義字符 | 意義 | ASCII碼值(十進制) |
---|---|---|
\a | 響鈴(BEL) | 007 |
\b | 退格(BS) ,將當前位置移到前一列 | 008 |
\f | 換頁(FF),將當前位置移到下頁開頭 | 012 |
\n | 換行(LF) ,將當前位置移到下一行開頭 | 010 |
\r | 回車(CR) ,將當前位置移到本行開頭 | 013 |
\t | 水平製表(HT) (跳到下一個TAB位置) | 009 |
\v | 垂直製表(VT) | 011 |
\ | 表明一個反斜線字符''' | 092 |
' | 表明一個單引號(撇號)字符 | 039 |
" | 表明一個雙引號字符 | 034 |
空字符(NULL) | 000 | |
\ddd | 1到3位八進制數所表明的任意字符 | 三位八進制 |
\xhh | 1到2位十六進制所表明的任意字符 | 二位十六進制 |
Lua 提供了不少的方法來支持字符串的操做:
序號 | 方法 & 用途 |
---|---|
1 | string.upper(argument): 字符串所有轉爲大寫字母。 |
2 | string.lower(argument): 字符串所有轉爲小寫字母。 |
3 | string.gsub(mainString,findString,replaceString,num) 在字符串中替換,mainString爲要替換的字符串, findString 爲被替換的字符,replaceString 要替換的字符,num 替換次數(能夠忽略,則所有替換),如: > string.gsub("aaaa","a","z",3); zzza 3 |
4 | string.find (str, substr, [init, [end]]) 在一個指定的目標字符串中搜索指定的內容(第三個參數爲索引),返回其具體位置。不存在則返回 nil。 > string.find("Hello Lua user", "Lua", 1) 7 9 |
5 | string.reverse(arg) 字符串反轉> string.reverse("Lua") auL |
6 | string.format(...) 返回一個相似printf的格式化字符串 > string.format("the value is:%d",4) the value is:4 |
7 | string.char(arg) 和 string.byte(arg[,int]) char 將整型數字轉成字符並鏈接, byte 轉換字符爲整數值(能夠指定某個字符,默認第一個字符)。 > string.char(97,98,99,100) abcd string.byte("ABCD",4) 68 string.byte("ABCD") 65 |
8 | string.len(arg) 計算字符串長度。 string.len("abc") 3 |
9 | string.rep(string, n)) 返回字符串string的n個拷貝 > string.rep("abcd",2) abcdabcd |
10 | .. 連接兩個字符串 > print("www.w3cschool"..".cn") www.w3cschool.cn |
如下實例演示瞭如何對字符串大小寫進行轉換:
string1 = "Lua"; print(string.upper(string1)) print(string.lower(string1))
以上代碼執行結果爲:
LUA lua
如下實例演示瞭如何對字符串進行查找與反轉操做:
string = "Lua Tutorial" -- 查找字符串 print(string.find(string,"Tutorial")) reversedString = string.reverse(string) print("新字符串爲",reversedString)
以上代碼執行結果爲:
5 12 新字符串爲 lairotuT auL
如下實例演示瞭如何對字符串進行格式化操做:
string1 = "Lua" string2 = "Tutorial" number1 = 10 number2 = 20 -- 基本字符串格式化 print(string.format("基本格式化 %s %s",string1,string2)) -- 日期格式化 date = 2; month = 1; year = 2014 print(string.format("日期格式化 %02d/%02d/%03d", date, month, year)) -- 十進制格式化 print(string.format("%.4f",1/3))
以上代碼執行結果爲:
基本格式化 Lua Tutorial 日期格式化 02/01/2014 0.3333
如下實例演示了字符與整數相互轉換:
-- 字符轉換 -- 轉換第一個字符 print(string.byte("Lua")) -- 轉換第三個字符 print(string.byte("Lua",3)) -- 轉換末尾第一個字符 print(string.byte("Lua",-1)) -- 第二個字符 print(string.byte("Lua",2)) -- 轉換末尾第二個字符 print(string.byte("Lua",-2)) -- 整數 ASCII 碼轉換爲字符 print(string.char(97))
以上代碼執行結果爲:
76 97 97 117 117 a
如下實例演示了其餘字符串操做,如計算字符串長度,字符串鏈接,字符串複製等:
string1 = "www." string2 = "w3cschool" string3 = ".cn" -- 使用 .. 進行字符串鏈接 print("鏈接字符串",string1..string2..string3) -- 字符串長度 print("字符串長度 ",string.len(string2)) -- 字符串複製 2 次 repeatedString = string.rep(string2,2) print(repeatedString)
以上代碼執行結果爲:
鏈接字符串 www.w3cschool.cn 字符串長度 9 w3cschoolw3cschool
由 sf0501 建立, 最後一次修改 2015-09-29
數組,就是相同數據類型的元素按必定順序排列的集合,能夠是一維數組和多維數組。
Lua 數組的索引鍵值能夠使用整數表示,數組的大小不是固定的。
一維數組是最簡單的數組,其邏輯結構是線性表。一維數組能夠用for循環出數組中的元素,以下實例:
array = {"Lua", "Tutorial"} for i= 0, 2 do print(array[i]) end
以上代碼執行輸出結果爲:
nil Lua Tutorial
正如你所看到的,咱們能夠使用整數索引來訪問數組元素,若是知道的索引沒有值則返回nil。
在 Lua 索引值是以 1 爲起始,但你也能夠指定 0 開始。
除此外咱們還能夠以負數爲數組索引值:
array = {} for i= -2, 2 do array[i] = i *2 end for i = -2,2 do print(array[i]) end
以上代碼執行輸出結果爲:
-4 -2 0 2 4
多維數組即數組中包含數組或一維數組的索引鍵對應一個數組。
如下是一個三行三列的陣列多維數組:
-- 初始化數組 array = {} for i=1,3 do array[i] = {} for j=1,3 do array[i][j] = i*j end end -- 訪問數組 for i=1,3 do for j=1,3 do print(array[i][j]) end end
以上代碼執行輸出結果爲:
1 2 3 2 4 6 3 6 9
不一樣索引鍵的三行三列陣列多維數組:
-- 初始化數組 array = {} maxRows = 3 maxColumns = 3 for row=1,maxRows do for col=1,maxColumns do array[row*maxColumns +col] = row*col end end -- 訪問數組 for row=1,maxRows do for col=1,maxColumns do print(array[row*maxColumns +col]) end end
以上代碼執行輸出結果爲:
1 2 3 2 4 6 3 6 9
正如你所看到的,以上的實例中,數組設定了指定的索引值,這樣能夠避免出現 nil 值,有利於節省內存空間。
由 sf0501 建立,youj 最後一次修改 2015-09-27
迭代器(iterator)是一種對象,它可以用來遍歷標準模板庫容器中的部分或所有元素,每一個迭代器對象表明容器中的肯定的地址
在Lua中迭代器是一種支持指針類型的結構,它能夠遍歷集合的每個元素。
泛型 for 在本身內部保存迭代函數,實際上它保存三個值:迭代函數、狀態常量、控制變量。
泛型 for 迭代器提供了集合的 key/value 對,語法格式以下:
for k, v in pairs(t) do print(k, v) end
上面代碼中,k, v爲變量列表;pairs(t)爲表達式列表。
查看如下實例:
array = {"Lua", "Tutorial"} for key,value in ipairs(array) do print(key, value) end
以上代碼執行輸出結果爲:
1 Lua 2 Tutorial
以上實例中咱們使用了 Lua 默認提供的迭代函數 ipairs。
下面咱們看看泛型for的執行過程:
。在Lua中咱們經常使用函數來描述迭代器,每次調用該函數就返回集合的下一個元素。Lua 的迭代器包含如下兩種類型:
無狀態的迭代器是指不保留任何狀態的迭代器,所以在循環中咱們能夠利用無狀態迭代器避免建立閉包花費額外的代價。
每一次迭代,迭代函數都是用兩個變量(狀態常量和控制變量)的值做爲參數被調用,一個無狀態的迭代器只利用這兩個值能夠獲取下一個元素。
這種無狀態迭代器的典型的簡單的例子是ipairs,他遍歷數組的每個元素。
如下實例咱們使用了一個簡單的函數來實現迭代器,實現 數字 n 的平方:
function square(iteratorMaxCount,currentNumber) if currentNumber<iteratorMaxCount then currentNumber = currentNumber+1 return currentNumber, currentNumber*currentNumber end end for i,n in square,3,0 do print(i,n) end
以上實例輸出結果爲:
1 1 2 4 3 9
迭代的狀態包括被遍歷的表(循環過程當中不會改變的狀態常量)和當前的索引下標(控制變量),ipairs和迭代函數都很簡單,咱們在Lua中能夠這樣實現:
function iter (a, i) i = i + 1 local v = a[i] if v then return i, v end end function ipairs (a) return iter, a, 0 end
當Lua調用ipairs(a)開始循環時,他獲取三個值:迭代函數iter、狀態常量a、控制變量初始值0;而後Lua調用iter(a,0)返回1,a[1](除非a[1]=nil);第二次迭代調用iter(a,1)返回2,a[2]……直到第一個nil元素。
不少狀況下,迭代器須要保存多個狀態信息而不是簡單的狀態常量和控制變量,最簡單的方法是使用閉包,還有一種方法就是將全部的狀態信息封裝到table內,將table做爲迭代器的狀態常量,由於這種狀況下能夠將全部的信息存放在table內,因此迭代函數一般不須要第二個參數。
如下實例咱們建立了本身的迭代器:
array = {"Lua", "Tutorial"} function elementIterator (collection) local index = 0 local count = #collection -- 閉包函數 return function () index = index + 1 if index <= count then -- 返回迭代器的當前元素 return collection[index] end end end for element in elementIterator(array) do print(element) end
以上實例輸出結果爲:
Lua Tutorial
以上實例中咱們能夠看到,elementIterator 內使用了閉包函數,實現計算集合大小並輸出各個元素。
由 sf0501 建立,youj 最後一次修改 2015-09-16
table 是 Lua 的一種數據結構用來幫助咱們建立不一樣的數據類型,如:數字、字典等。
Lua table 使用關聯型數組,你能夠用任意類型的值來做數組的索引,但這個值不能是 nil。
Lua table 是不固定大小的,你能夠根據本身須要進行擴容。
Lua也是經過table來解決模塊(module)、包(package)和對象(Object)的。 例如string.format表示使用"format"來索引table string。
構造器是建立和初始化表的表達式。表是Lua特有的功能強大的東西。最簡單的構造函數是{},用來建立一個空表。能夠直接初始化數組:
-- 初始化表 mytable = {} -- 指定值 mytable[1]= "Lua" -- 移除引用 mytable = nil -- lua 垃圾回收會釋放內存
當咱們爲 table a 並設置元素,而後將 a 賦值給 b,則 a 與 b 都指向同一個內存。若是 a 設置爲 nil ,則 b 一樣能訪問 table 的元素。若是沒有指定的變量指向a,Lua的垃圾回收機制會清理相對應的內存。
如下實例演示了以上的描述狀況:
-- 簡單的 table mytable = {} print("mytable 的類型是 ",type(mytable)) mytable[1]= "Lua" mytable["wow"] = "修改前" print("mytable 索引爲 1 的元素是 ", mytable[1]) print("mytable 索引爲 wow 的元素是 ", mytable["wow"]) -- alternatetable和mytable的是指同一個 table alternatetable = mytable print("alternatetable 索引爲 1 的元素是 ", alternatetable[1]) print("mytable 索引爲 wow 的元素是 ", alternatetable["wow"]) alternatetable["wow"] = "修改後" print("mytable 索引爲 wow 的元素是 ", mytable["wow"]) -- 釋放變量 alternatetable = nil print("alternatetable 是 ", alternatetable) -- mytable 仍然能夠訪問 print("mytable 索引爲 wow 的元素是 ", mytable["wow"]) mytable = nil print("mytable 是 ", mytable)
以上代碼執行結果爲:
mytable 的類型是 table mytable 索引爲 1 的元素是 Lua mytable 索引爲 wow 的元素是 修改前 alternatetable 索引爲 1 的元素是 Lua mytable 索引爲 wow 的元素是 修改前 mytable 索引爲 wow 的元素是 修改後 alternatetable 是 nil mytable 索引爲 wow 的元素是 修改後 mytable 是 nil
如下列出了 Table 操做經常使用的方法:
序號 | 方法 & 用途 |
---|---|
1 | table.concat (table [, step [, start [, end]]]):concat是concatenate(連鎖, 鏈接)的縮寫. table.concat()函數列出參數中指定table的數組部分從start位置到end位置的全部元素, 元素間以指定的分隔符(sep)隔開。 |
2 | table.insert (table, [pos,] value):在table的數組部分指定位置(pos)插入值爲value的一個元素. pos參數可選, 默認爲數組部分末尾. |
3 | table.maxn (table)指定table中全部正數key值中最大的key值. 若是不存在key值爲正數的元素, 則返回0。(Lua5.2以後該方法已經不存在了,本文使用了自定義函數實現) |
4 | table.remove (table [, pos])返回table數組部分位於pos位置的元素. 其後的元素會被前移. pos參數可選, 默認爲table長度, 即從最後一個元素刪起。 |
5 | table.sort (table [, comp])對給定的table進行升序排序。 |
接下來咱們來看下這幾個方法的實例。
咱們能夠使用 concat() 方法來鏈接兩個 table:
fruits = {"banana","orange","apple"} -- 返回 table 鏈接後的字符串 print("鏈接後的字符串 ",table.concat(fruits)) -- 指定鏈接字符 print("鏈接後的字符串 ",table.concat(fruits,", ")) -- 指定索引來鏈接 table print("鏈接後的字符串 ",table.concat(fruits,", ", 2,3))
執行以上代碼輸出結果爲:
鏈接後的字符串 bananaorangeapple 鏈接後的字符串 banana, orange, apple 鏈接後的字符串 orange, apple
如下實例演示了 table 的插入和移除操做:
fruits = {"banana","orange","apple"} -- 在末尾插入 table.insert(fruits,"mango") print("索引爲 4 的元素爲 ",fruits[4]) -- 在索引爲 2 的鍵處插入 table.insert(fruits,2,"grapes") print("索引爲 2 的元素爲 ",fruits[2]) print("最後一個元素爲 ",fruits[5]) table.remove(fruits) print("移除後最後一個元素爲 ",fruits[5])
執行以上代碼輸出結果爲:
索引爲 4 的元素爲 mango 索引爲 2 的元素爲 grapes 最後一個元素爲 mango 移除後最後一個元素爲 nil
如下實例演示了 sort() 方法的使用,用於對 Table 進行排序:
fruits = {"banana","orange","apple","grapes"} print("排序前") for k,v in ipairs(fruits) do print(k,v) end table.sort(fruits) print("排序後") for k,v in ipairs(fruits) do print(k,v) end
執行以上代碼輸出結果爲:
排序前 1 banana 2 orange 3 apple 4 grapes 排序後 1 apple 2 banana 3 grapes 4 orange
table.maxn 在 Lua5.2 以後該方法已經不存在了,咱們定義了 table_maxn 方法來實現。
如下實例演示瞭如何獲取 table 中的最大值:
function table_maxn(t) local mn = 0 for k, v in pairs(t) do if mn < k then mn = k end end return mn end tbl = {[1] = "a", [2] = "b", [3] = "c", [26] = "z"} print("tbl 長度 ", #tbl) print("tbl 最大值 ", table_maxn(tbl))
執行以上代碼輸出結果爲:
tbl 長度 3 tbl 最大值 26
由 sf0501 建立, 最後一次修改 2015-09-08
模塊相似於一個封裝庫,從 Lua 5.1 開始,Lua 加入了標準的模塊管理機制,能夠把一些公用的代碼放在一個文件裏,以 API 接口的形式在其餘地方調用,有利於代碼的重用和下降代碼耦合度。
Lua 的模塊是由變量、函數等已知元素組成的 table,所以建立一個模塊很簡單,就是建立一個 table,而後把須要導出的常量、函數放入其中,最後返回這個 table 就行。如下爲建立自定義模塊 module.lua,文件代碼格式以下:
-- 文件名爲 module.lua -- 定義一個名爲 module 的模塊 module = {} -- 定義一個常量 module.constant = "這是一個常量" -- 定義一個函數 function module.func1() io.write("這是一個公有函數!\n") end local function func2() print("這是一個私有函數!") end function module.func3() func2() end return module
由上可知,模塊的結構就是一個 table 的結構,所以能夠像操做調用 table 裏的元素那樣來操做調用模塊裏的常量或函數。
上面的 func2 聲明爲程序塊的局部變量,即表示一個私有函數,所以是不能從外部訪問模塊裏的這個私有函數,必須經過模塊裏的公有函數來調用.
Lua提供了一個名爲require的函數用來加載模塊。要加載一個模塊,只須要簡單地調用就能夠了。例如:
require("<模塊名>")
或者
require "<模塊名>"
執行 require 後會返回一個由模塊常量或函數組成的 table,而且還會定義一個包含該 table 的全局變量。
-- test_module.php 文件 -- module 模塊爲上文提到到 module.lua require("module") print(module.constant) module.func3()
以上代碼執行結果爲:
這是一個常量 這是一個私有函數!
或者給加載的模塊定義一個別名變量,方便調用:
-- test_module2.php 文件 -- module 模塊爲上文提到到 module.lua -- 別名變量 m local m = require("module") print(m.constant) m.func3()
以上代碼執行結果爲:
這是一個常量 這是一個私有函數!
對於自定義的模塊,模塊文件不是放在哪一個文件目錄都行,函數 require 有它本身的文件路徑加載策略,它會嘗試從 Lua 文件或 C 程序庫中加載模塊。
require 用於搜索 Lua 文件的路徑是存放在全局變量 package.path 中,當 Lua 啓動後,會以環境變量 LUA_PATH 的值來初始這個環境變量。若是沒有找到該環境變量,則使用一個編譯時定義的默認路徑來初始化。
固然,若是沒有 LUA_PATH 這個環境變量,也能夠自定義設置,在當前用戶根目錄下打開 .profile 文件(沒有則建立,打開 .bashrc 文件也能夠),例如把 "~/lua/" 路徑加入 LUA_PATH 環境變量裏:
#LUA_PATH export LUA_PATH="~/lua/?.lua;;"
文件路徑以 ";" 號分隔,最後的 2 個 ";;" 表示新加的路徑後面加上原來的默認路徑。
接着,更新環境變量參數,使之當即生效。
source ~/.profile
這時假設 package.path 的值是:
/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua
那麼調用 require("module") 時就會嘗試打開如下文件目錄去搜索目標。
/Users/dengjoe/lua/module.lua; ./module.lua /usr/local/share/lua/5.1/module.lua /usr/local/share/lua/5.1/module/init.lua /usr/local/lib/lua/5.1/module.lua /usr/local/lib/lua/5.1/module/init.lua
若是找過目標文件,則會調用 package.loadfile 來加載模塊。不然,就會去找 C 程序庫。
搜索的文件路徑是從全局變量 package.cpath 獲取,而這個變量則是經過環境變量 LUA_CPATH 來初始。
搜索的策略跟上面的同樣,只不過如今換成搜索的是 so 或 dll 類型的文件。若是找獲得,那麼 require 就會經過 package.loadlib 來加載它。
Lua和C是很容易結合的,使用C爲Lua寫包。
與Lua中寫包不一樣,C包在使用之前必須首先加載並鏈接,在大多數系統中最容易的實現方式是經過動態鏈接庫機制。
Lua在一個叫loadlib的函數內提供了全部的動態鏈接的功能。這個函數有兩個參數:庫的絕對路徑和初始化函數。因此典型的調用的例子以下:
local path = "/usr/local/lua/lib/libluasocket.so" local f = loadlib(path, "luaopen_socket")
loadlib函數加載指定的庫而且鏈接到Lua,然而它並不打開庫(也就是說沒有調用初始化函數),反之他返回初始化函數做爲Lua的一個函數,這樣咱們就能夠直接在Lua中調用他。
若是加載動態庫或者查找初始化函數時出錯,loadlib將返回nil和錯誤信息。咱們能夠修改前面一段代碼,使其檢測錯誤而後調用初始化函數:
local path = "/usr/local/lua/lib/libluasocket.so" -- 或者 path = "C:\\windows\\luasocket.dll",這是 Window 平臺下 local f = assert(loadlib(path, "luaopen_socket")) f() -- 真正打開庫
通常狀況下咱們指望二進制的發佈庫包含一個與前面代碼段類似的stub文件,安裝二進制庫的時候能夠隨便放在某個目錄,只須要修改stub文件對應二進制庫的實際路徑便可。
將stub文件所在的目錄加入到LUA_PATH,這樣設定後就能夠使用require函數加載C庫了。
由 sf0501 建立,youj 最後一次修改 2015-09-07
在 Lua table 中咱們能夠訪問對應的key來獲得value值,可是卻沒法對兩個 table 進行操做。
所以 Lua 提供了元表(Metatable),容許咱們改變table的行爲,每一個行爲關聯了對應的元方法。
例如,使用元表咱們能夠定義Lua如何計算兩個table的相加操做a+b。
當Lua試圖對兩個表進行相加時,先檢查二者之一是否有元表,以後檢查是否有一個叫"__add"的字段,若找到,則調用對應的值。"__add"等即時字段,其對應的值(每每是一個函數或是table)就是"元方法"。
有兩個很重要的函數來處理元表:
如下實例演示瞭如何對指定的表設置元表:
mytable = {} -- 普通表 mymetatable = {} -- 元表 setmetatable(mytable,mymetatable) -- 把 mymetatable 設爲 mytable 的元表
以上代碼也能夠直接寫成一行:
mytable = setmetatable({},{})
如下爲返回對象元表:
getmetatable(mytable) -- 這回返回mymetatable
這是 metatable 最經常使用的鍵。
當你經過鍵來訪問 table 的時候,若是這個鍵沒有值,那麼Lua就會尋找該table的metatable(假定有metatable)中的__index 鍵。若是__index包含一個表格,Lua會在表格中查找相應的鍵。
咱們能夠在使用 lua 命令進入交互模式查看:
$ lua Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio > other = { foo = 3 } > t = setmetatable({}, { __index = other }) > t.foo 3 > t.bar nil
若是__index包含一個函數的話,Lua就會調用那個函數,table和鍵會做爲參數傳遞給函數。
__index 元方法查看錶中元素是否存在,若是不存在,返回結果爲 nil;若是存在則由 __index 返回結果。
mytable = setmetatable({key1 = "value1"}, { __index = function(mytable, key) if key == "key2" then return "metatablevalue" else return nil end end }) print(mytable.key1,mytable.key2)
實例輸出結果爲:
value1 metatablevalue
實例解析:
mytable 表賦值爲 {key1 = "value1"}。
mytable 設置了元表,元方法爲 __index。
在mytable表中查找 key1,若是找到,返回該元素,找不到則繼續。
在mytable表中查找 key2,若是找到,返回該元素,找不到則繼續。
判斷元表有沒有__index方法,若是__index方法是一個函數,則調用該函數。
元方法中查看是否傳入 "key2" 鍵的參數(mytable.key2已設置),若是傳入 "key2" 參數返回 "metatablevalue",不然返回 mytable 對應的鍵值。
咱們能夠將以上代碼簡單寫成:
mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } }) print(mytable.key1,mytable.key2)
__newindex 元方法用來對錶更新,__index則用來對錶訪問 。
當你給表的一個缺乏的索引賦值,解釋器就會查找__newindex 元方法:若是存在則調用這個函數而不進行賦值操做。
如下實例演示了 __newindex 元方法的應用:
mymetatable = {} mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable }) print(mytable.key1) mytable.newkey = "新值2" print(mytable.newkey,mymetatable.newkey) mytable.key1 = "新值1" print(mytable.key1,mymetatable.newkey1)
以上實例執行輸出結果爲:
value1 nil 新值2 新值1 nil
以上實例中表設置了元方法 __newindex,在對新索引鍵(newkey)賦值時(mytable.newkey = "新值2"),會調用元方法,而不進行賦值。而若是對已存在的索引鍵(key1),則會進行賦值,而不調用元方法 __newindex。
如下實例使用了 rawset 函數來更新表:
mytable = setmetatable({key1 = "value1"}, { __newindex = function(mytable, key, value) rawset(mytable, key, "\""..value.."\"") end }) mytable.key1 = "new value" mytable.key2 = 4 print(mytable.key1,mytable.key2)
以上實例執行輸出結果爲:
new value "4"
如下實例演示了兩表相加操做:
-- 計算表中最大值,table.maxn在Lua5.2以上版本中已沒法使用 -- 自定義計算表中最大值函數 table_maxn function table_maxn(t) local mn = 0 for k, v in pairs(t) do if mn < k then mn = k end end return mn end -- 兩表相加操做 mytable = setmetatable({ 1, 2, 3 }, { __add = function(mytable, newtable) for i = 1, table_maxn(newtable) do table.insert(mytable, table_maxn(mytable)+1,newtable[i]) end return mytable end }) secondtable = {4,5,6} mytable = mytable + secondtable for k,v in ipairs(mytable) do print(k,v) end
以上實例執行輸出結果爲:
1 1 2 2 3 3 4 4 5 5 6 6
__add 鍵包含在元表中,並進行相加操做。 表中對應的操做列表以下:
模式 | 描述 |
---|---|
__add | 對應的運算符 '+'. |
__sub | 對應的運算符 '-'. |
__mul | 對應的運算符 '*'. |
__div | 對應的運算符 '/'. |
__mod | 對應的運算符 '%'. |
__unm | 對應的運算符 '-'. |
__concat | 對應的運算符 '..'. |
__eq | 對應的運算符 '=='. |
__lt | 對應的運算符 '<'. |
__le | 對應的運算符 '<='. |
__call 元方法在 Lua 調用一個值時調用。如下實例演示了計算表中元素的和:
-- 計算表中最大值,table.maxn在Lua5.2以上版本中已沒法使用 -- 自定義計算表中最大值函數 table_maxn function table_maxn(t) local mn = 0 for k, v in pairs(t) do if mn < k then mn = k end end return mn end -- 定義元方法__call mytable = setmetatable({10}, { __call = function(mytable, newtable) sum = 0 for i = 1, table_maxn(mytable) do sum = sum + mytable[i] end for i = 1, table_maxn(newtable) do sum = sum + newtable[i] end return sum end }) newtable = {10,20,30} print(mytable(newtable))
以上實例執行輸出結果爲:
70
__tostring 元方法用於修改表的輸出行爲。如下實例咱們自定義了表的輸出內容:
mytable = setmetatable({ 10, 20, 30 }, { __tostring = function(mytable) sum = 0 for k, v in pairs(mytable) do sum = sum + v end return "表全部元素的和爲 " .. sum end }) print(mytable)
以上實例執行輸出結果爲:
表全部元素的和爲 60
從本文中咱們能夠知道元表能夠很好的簡化咱們的代碼功能,因此瞭解 Lua 的元表,可讓咱們寫出更加簡單優秀的 Lua 代碼。
由 sf0501 建立, 最後一次修改 2015-09-26
Lua 協同程序(coroutine)與線程比較相似:擁有獨立的堆棧,獨立的局部變量,獨立的指令指針,同時又與其它協同程序共享全局變量和其它大部分東西。
協同是很是強大的功能,可是用起來也很複雜。
線程與協同程序的主要區別在於,一個具備多個線程的程序能夠同時運行幾個線程,而協同程序卻須要彼此協做的運行。
在任一指定時刻只有一個協同程序在運行,而且這個正在運行的協同程序只有在明確的被要求掛起的時候纔會被掛起。
協同程序有點相似同步的多線程,在等待同一個線程鎖的幾個線程有點相似協同。
方法 | 描述 |
---|---|
coroutine.create() | 建立coroutine,返回coroutine, 參數是一個函數,當和resume配合使用的時候就喚醒函數調用 |
coroutine.resume() | 重啓coroutine,和create配合使用 |
coroutine.yield() | 掛起coroutine,將coroutine設置爲掛起狀態,這個和resume配合使用能有不少有用的效果 |
coroutine.status() | 查看coroutine的狀態 注:coroutine的狀態有三種:dead,suspend,running,具體何時有這樣的狀態請參考下面的程序 |
coroutine.wrap() | 建立coroutine,返回一個函數,一旦你調用這個函數,就進入coroutine,和create功能重複 |
coroutine.running() | 返回正在跑的coroutine,一個coroutine就是一個線程,當使用running的時候,就是返回一個corouting的線程號 |
-- coroutine_test.lua 文件 co = coroutine.create( function(i) print(i); end ) coroutine.resume(co, 1) -- 1 print(coroutine.status(co)) -- dead print("----------") co = coroutine.wrap( function(i) print(i); end ) co(1) print("----------") co2 = coroutine.create( function() for i=1,10 do print(i) if i == 3 then print(coroutine.status(co2)) --running print(coroutine.running()) --thread:XXXXXX end coroutine.yield() end end ) coroutine.resume(co2) --1 coroutine.resume(co2) --2 coroutine.resume(co2) --3 print(coroutine.status(co2)) -- suspended print(coroutine.running()) --nil print("----------")
以上實例執行輸出結果爲:
1 dead ---------- 1 ---------- 1 2 3 running thread: 0x7fb801c05868 false suspended thread: 0x7fb801c04c88 true ----------
coroutine.running就能夠看出來,coroutine在底層實現就是一個線程。
當create一個coroutine的時候就是在新線程中註冊了一個事件。
當使用resume觸發事件的時候,create的coroutine函數就被執行了,當遇到yield的時候就表明掛起當前線程,等候再次resume觸發事件。
接下來咱們分析一個更詳細的實例:
function foo (a) print("foo 函數輸出", a) return coroutine.yield(2 * a) -- 返回 2*a 的值 end co = coroutine.create(function (a , b) print("第一次協同程序執行輸出", a, b) -- co-body 1 10 local r = foo(a + 1) print("第二次協同程序執行輸出", r) local r, s = coroutine.yield(a + b, a - b) -- a,b的值爲第一次調用協同程序時傳入 print("第三次協同程序執行輸出", r, s) return b, "結束協同程序" -- b的值爲第二次調用協同程序時傳入 end) print("main", coroutine.resume(co, 1, 10)) -- true, 4 print("--分割線----") print("main", coroutine.resume(co, "r")) -- true 11 -9 print("---分割線---") print("main", coroutine.resume(co, "x", "y")) -- true 10 end print("---分割線---") print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine print("---分割線---")
以上實例執行輸出結果爲:
第一次協同程序執行輸出 1 10 foo 函數輸出 2 main true 4 --分割線---- 第二次協同程序執行輸出 r main true 11 -9 ---分割線--- 第三次協同程序執行輸出 x y main true 10 結束協同程序 ---分割線--- main false cannot resume dead coroutine ---分割線---
以上實例接下以下:
resume和yield的配合強大之處在於,resume處於主程中,它將外部狀態(數據)傳入到協同程序內部;而yield則將內部的狀態(數據)返回到主程中。
如今我就使用Lua的協同程序來完成生產者-消費者這一經典問題。
local newProductor function productor() local i = 0 while true do i = i + 1 send(i) -- 將生產的物品發送給消費者 end end function consumer() while true do local i = receive() -- 從生產者那裏獲得物品 print(i) end end function receive() local status, value = coroutine.resume(newProductor) return value end function send(x) coroutine.yield(x) -- x表示須要發送的值,值返回之後,就掛起該協同程序 end -- 啓動程序 newProductor = coroutine.create(productor) consumer() local newProductor function productor() local i = 0 while true do i = i + 1 send(i) -- 將生產的物品發送給消費者 end end function consumer() while true do local i = receive() -- 從生產者那裏獲得物品 print(i) end end function receive() local status, value = coroutine.resume(newProductor) return value end function send(x) coroutine.yield(x) -- x表示須要發送的值,值返回之後,就掛起該協同程序 end -- 啓動程序 newProductor = coroutine.create(productor) consumer()
以上實例執行輸出結果爲:
1 2 3 4 5 6 7 8 9 10 11 12 13 ……
由 sf0501 建立, 最後一次修改 2015-09-09
Lua I/O 庫用於讀取和處理文件。分爲簡單模式(和C同樣)、徹底模式。
簡單模式在作一些簡單的文件操做時較爲合適。可是在進行一些高級的文件操做的時候,簡單模式就顯得力不從心。例如同時讀取多個文件這樣的操做,使用徹底模式則較爲合適。
打開文件操做語句以下:
file = io.open (filename [, mode])
mode 的值有:
模式 | 描述 |
---|---|
r | 以只讀方式打開文件,該文件必須存在。 |
w | 打開只寫文件,若文件存在則文件長度清爲0,即該文件內容會消失。若文件不存在則創建該文件。 |
a | 以附加的方式打開只寫文件。若文件不存在,則會創建該文件,若是文件存在,寫入的數據會被加到文件尾,即文件原先的內容會被保留。(EOF符保留) |
r+ | 以可讀寫方式打開文件,該文件必須存在。 |
w+ | 打開可讀寫文件,若文件存在則文件長度清爲零,即該文件內容會消失。若文件不存在則創建該文件。 |
a+ | 與a相似,但此文件可讀可寫 |
b | 二進制模式,若是文件是二進制文件,能夠加上b |
+ | 號表示對文件既能夠讀也能夠寫 |
簡單模式使用標準的 I/O 或使用一個當前輸入文件和一個當前輸出文件。
如下爲 file.lua 文件代碼,操做的文件爲test.lua(若是沒有你須要建立該文件),代碼以下:
-- 以只讀方式打開文件 file = io.open("test.lua", "r") -- 設置默認輸入文件爲 test.lua io.input(file) -- 輸出文件第一行 print(io.read()) -- 關閉打開的文件 io.close(file) -- 以附加的方式打開只寫文件 file = io.open("test.lua", "a") -- 設置默認輸出文件爲 test.lua io.output(file) -- 在文件最後一行添加 Lua 註釋 io.write("-- test.lua 文件末尾註釋") -- 關閉打開的文件 io.close(file)
執行以上代碼,你會發現,輸出了 test.ua 文件的第一行信息,並在該文件最後一行添加了 lua 的註釋。如我這邊輸出的是:
-- test.lua 文件
在以上實例中咱們使用了 io."x" 方法,其中 io.read() 中咱們沒有帶參數,參數能夠是下表中的一個:
模式 | 描述 |
---|---|
"*n" | 讀取一個數字並返回它。例:file.read("*n") |
"*a" | 從當前位置讀取整個文件。例:file.read("*a") |
"*l"(默認) | 讀取下一行,在文件尾 (EOF) 處返回 nil。例:file.read("*l") |
number | 返回一個指定字符個數的字符串,或在 EOF 時返回 nil。例:file.read(5) |
其餘的 io 方法有:
一般咱們須要在同一時間處理多個文件。咱們須要使用 file:function_name 來代替 io.function_name 方法。如下實例演示瞭如何同時處理同一個文件:
-- 以只讀方式打開文件 file = io.open("test.lua", "r") -- 輸出文件第一行 print(file:read()) -- 關閉打開的文件 file:close() -- 以附加的方式打開只寫文件 file = io.open("test.lua", "a") -- 在文件最後一行添加 Lua 註釋 file:write("--test") -- 關閉打開的文件 file:close()
執行以上代碼,你會發現,輸出了 test.ua 文件的第一行信息,並在該文件最後一行添加了 lua 的註釋。如我這邊輸出的是:
-- test.lua 文件
read 的參數與簡單模式一致。
其餘方法:
file:seek(optional whence, optional offset): 設置和獲取當前文件位置,成功則返回最終的文件位置(按字節),失敗則返回nil加錯誤信息。參數 whence 值能夠是:
不帶參數file:seek()則返回當前位置,file:seek("set")則定位到文件頭,file:seek("end")則定位到文件尾並返回文件大小
file:flush(): 向文件寫入緩衝中的全部數據
io.lines(optional file name): 打開指定的文件filename爲讀模式並返回一個迭代函數,每次調用將得到文件中的一行內容,當到文件尾時,將返回nil,並自動關閉文件。
若不帶參數時io.lines() io.input():lines(); 讀取默認輸入設備的內容,但結束時不關閉文件,如
for line in io.lines("main.lua") do print(line) end
如下實例使用了 seek 方法,定位到文件倒數第 25 個位置並使用 read 方法的 *a 參數,即從當期位置(倒數第 25 個位置)讀取整個文件。
-- 以只讀方式打開文件 file = io.open("test.lua", "r") file:seek("end",-25) print(file:read("*a")) -- 關閉打開的文件 file:close()
我這邊輸出的結果是:
st.lua 文件末尾--test
由 sf0501 建立,youj 最後一次修改 2015-09-12
程序運行中錯誤處理是必要的,在咱們進行文件操做,數據轉移及web service 調用過程當中都會出現不可預期的錯誤。若是不注重錯誤信息的處理,就會形成信息泄露,程序沒法運行等狀況。
任何程序語言中,都須要錯誤處理。錯誤類型有:
語法錯誤一般是因爲對程序的組件(如運算符、表達式)使用不當引發的。一個簡單的實例以下:
-- test.lua 文件 a == 2
以上代碼執行結果爲:
lua: test.lua:2: syntax error near '=='
正如你所看到的,以上出現了語法錯誤,一個 "=" 號跟兩個 "=" 號是有區別的。一個 "=" 是賦值表達式兩個 "=" 是比較運算。
另一個實例:
for a= 1,10 print(a) end
執行以上程序會出現以下錯誤:
lua: test2.lua:2: 'do' expected near 'print'
語法錯誤比程序運行錯誤更簡單,運行錯誤沒法定位具體錯誤,而語法錯誤咱們能夠很快的解決,如以上實例咱們只要在for語句下添加 do 便可:
for a= 1,10 do print(a) end
運行錯誤是程序能夠正常執行,可是會輸出報錯信息。以下實例因爲參數輸入錯誤,程序執行時報錯:
function add(a,b) return a+b end add(10)
當咱們編譯運行如下代碼時,編譯是能夠成功的,但在運行的時候會產生以下錯誤:
lua: test2.lua:2: attempt to perform arithmetic on local 'b' (a nil value) stack traceback: test2.lua:2: in function 'add' test2.lua:5: in main chunk [C]: ?
如下報錯信息是因爲程序缺乏 b 參數引發的。
咱們能夠使用兩個函數:assert 和 error 來處理錯誤。實例以下:
local function add(a,b) assert(type(a) == "number", "a 不是一個數字") assert(type(b) == "number", "b 不是一個數字") return a+b end add(10)
執行以上程序會出現以下錯誤:
lua: test.lua:3: b 不是一個數字 stack traceback: [C]: in function 'assert' test.lua:3: in local 'add' test.lua:6: in main chunk [C]: in ?
實例中assert首先檢查第一個參數,若沒問題,assert不作任何事情;不然,assert以第二個參數做爲錯誤信息拋出。
語法格式:
error (message [, level])
功能:終止正在執行的函數,並返回message的內容做爲錯誤信息(error函數永遠都不會返回)
一般狀況下,error會附加一些錯誤位置的信息到message頭部。
Level參數指示得到錯誤的位置:
Lua中處理錯誤,能夠使用函數pcall(protected call)來包裝須要執行的代碼。
pcall接收一個函數和要傳遞個後者的參數,並執行,執行結果:有錯誤、無錯誤;返回值true或者或false, errorinfo。
語法格式以下
if pcall(function_name, ….) then -- 沒有錯誤 else -- 一些錯誤 end
簡單實例:
> =pcall(function(i) print(i) end, 33) 33 true > =pcall(function(i) print(i) error('error..') end, 33) 33 false stdin:1: error.. > function f() return false,2 end > if f() then print '1' else print '0' end 0
pcall以一種"保護模式"來調用第一個參數,所以pcall能夠捕獲函數執行中的任何錯誤。
一般在錯誤發生時,但願落得更多的調試信息,而不僅是發生錯誤的位置。但pcall返回時,它已經銷燬了調用桟的部份內容。
Lua提供了xpcall函數,xpcall接收第二個參數——一個錯誤處理函數,當錯誤發生時,Lua會在調用桟展看(unwind)前調用錯誤處理函數,因而就能夠在這個函數中使用debug庫來獲取關於錯誤的額外信息了。
debug庫提供了兩個通用的錯誤處理函數:
>=xpcall(function(i) print(i) error('error..') end, function() print(debug.traceback()) end, 33) 33 stack traceback: stdin:1: in function [C]: in function 'error' stdin:1: in function [C]: in function 'xpcall' stdin:1: in main chunk [C]: in ? false nil
xpcall 使用實例 2:
function myfunction () n = n/nil end function myerrorhandler( err ) print( "ERROR:", err ) end status = xpcall( myfunction, myerrorhandler ) print( status)
執行以上程序會出現以下錯誤:
ERROR: test2.lua:2: attempt to perform arithmetic on global 'n' (a nil value) false
由 sf0501 建立, 最後一次修改 2015-09-26
Lua 提供了 debug 庫用於提供建立咱們自定義調速器的功能。Lua 自己並未有內置的調速器,但不少開發者共享了他們的 Lua 調速器代碼。
Lua 中 debug 庫包含如下函數:
sethook ([thread,] hook, mask [, count]):
序號 | 方法 & 用途 |
---|---|
1. | debug(): 進入一個用戶交互模式,運行用戶輸入的每一個字符串。 使用簡單的命令以及其它調試設置,用戶能夠檢閱全局變量和局部變量, 改變變量的值,計算一些表達式,等等。 輸入一行僅包含 cont 的字符串將結束這個函數, 這樣調用者就能夠繼續向下運行。 |
2. | getfenv(object): 返回對象的環境變量。 |
3. | gethook(optional thread): 返回三個表示線程鉤子設置的值: 當前鉤子函數,當前鉤子掩碼,當前鉤子計數 |
4. | getinfo ([thread,] f [, what]): 返回關於一個函數信息的表。 你能夠直接提供該函數, 也能夠用一個數字 f 表示該函數。 數字 f 表示運行在指定線程的調用棧對應層次上的函數: 0 層表示當前函數(getinfo 自身); 1 層表示調用 getinfo 的函數 (除非是尾調用,這種狀況不計入棧);等等。 若是 f 是一個比活動函數數量還大的數字, getinfo 返回 nil。 |
5. | debug.getlocal ([thread,] f, local): 此函數返回在棧的 f 層處函數的索引爲 local 的局部變量 的名字和值。 這個函數不只用於訪問顯式定義的局部變量,也包括形參、臨時變量等。 |
6. | getmetatable(value): 把給定索引指向的值的元表壓入堆棧。若是索引無效,或是這個值沒有元表,函數將返回 0 而且不會向棧上壓任何東西。 |
7. | getregistry(): 返回註冊表表,這是一個預約義出來的表, 能夠用來保存任何 C 代碼想保存的 Lua 值。 |
8. | getupvalue (f, up)此函數返回函數 f 的第 up 個上值的名字和值。 若是該函數沒有那個上值,返回 nil 。 以 '(' (開括號)打頭的變量名錶示沒有名字的變量 (去除了調試信息的代碼塊)。 |
10. | 將一個函數做爲鉤子函數設入。 字符串 mask 以及數字 count 決定了鉤子將在什麼時候調用。 掩碼是由下列字符組合成的字符串,每一個字符有其含義: 'c': 每當 Lua 調用一個函數時,調用鉤子; 'r': 每當 Lua 從一個函數內返回時,調用鉤子; 'l': 每當 Lua 進入新的一行時,調用鉤子。 |
11. | setlocal ([thread,] level, local, value): 這個函數將 value 賦給 棧上第 level 層函數的第 local 個局部變量。 若是沒有那個變量,函數返回 nil 。 若是 level 越界,拋出一個錯誤。 |
12. | setmetatable (value, table): 將 value 的元表設爲 table (能夠是 nil)。 返回 value。 |
13. | setupvalue (f, up, value): 這個函數將 value 設爲函數 f 的第 up 個上值。 若是函數沒有那個上值,返回 nil 不然,返回該上值的名字。 |
14. | traceback ([thread,] [message [, level]]):若是 message 有,且不是字符串或 nil, 函數不作任何處理直接返回 message。 不然,它返回調用棧的棧回溯信息。 字符串可選項 message 被添加在棧回溯信息的開頭。 數字可選項 level 指明從棧的哪一層開始回溯 (默認爲 1 ,即調用 traceback 的那裏)。 |
上表列出了咱們經常使用的調試函數,接下來咱們能夠看些簡單的例子:
function myfunction () print(debug.traceback("Stack trace")) print(debug.getinfo(1)) print("Stack trace end") return 10 end myfunction () print(debug.getinfo(1))
執行以上代碼輸出結果爲:
Stack trace stack traceback: test2.lua:2: in function 'myfunction' test2.lua:8: in main chunk [C]: ? table: 0054C6C8 Stack trace end
在以實例中,咱們使用到了 debug 庫的 traceback 和 getinfo 函數, getinfo 函數用於返回函數信息的表。
咱們常常須要調試函數的內的局部變量。咱們能夠使用 getupvalue 函數來設置這些局部變量。實例以下:
function newCounter () local n = 0 local k = 0 return function () k = n n = n + 1 return n end end counter = newCounter () print(counter()) print(counter()) local i = 1 repeat name, val = debug.getupvalue(counter, i) if name then print ("index", i, name, "=", val) if(name == "n") then debug.setupvalue (counter,2,10) end i = i + 1 end -- if until not name print(counter())
執行以上代碼輸出結果爲:
1 2 index 1 k = 1 index 2 n = 2 11
在以上實例中,計數器在每次調用時都會自增1。實例中咱們使用了 getupvalue 函數查看局部變量的當前狀態。咱們能夠設置局部變量爲新值。實例中,在設置前 n 的值爲 2,使用 setupvalue 函數將其設置爲 10。如今咱們調用函數,執行後輸出爲 11 而不是 3。
命令行調試器有:RemDebug、clidebugger、ctrace、xdbLua、LuaInterface - Debugger、Rldb、ModDebug。
圖形界調試器有:SciTE、Decoda、ZeroBrane Studio、akdebugger、luaedit。
由 sf0501 建立, 最後一次修改 2015-09-28
Lua 採用了自動內存管理。 這意味着你不用操心新建立的對象須要的內存如何分配出來, 也不用考慮在對象再也不被使用後怎樣釋放它們所佔用的內存。
Lua 運行了一個垃圾收集器來收集全部死對象 (即在 Lua 中不可能再訪問到的對象)來完成自動內存管理的工做。 Lua 中全部用到的內存,如:字符串、表、用戶數據、函數、線程、 內部結構等,都服從自動管理。
Lua 實現了一個增量標記-掃描收集器。 它使用這兩個數字來控制垃圾收集循環: 垃圾收集器間歇率和垃圾收集器步進倍率。 這兩個數字都使用百分數爲單位 (例如:值 100 在內部表示 1 )。
垃圾收集器間歇率控制着收集器須要在開啓新的循環前要等待多久。 增大這個值會減小收集器的積極性。 當這個值比 100 小的時候,收集器在開啓新的循環前不會有等待。 設置這個值爲 200 就會讓收集器等到總內存使用量達到 以前的兩倍時纔開始新的循環。
垃圾收集器步進倍率控制着收集器運做速度相對於內存分配速度的倍率。 增大這個值不只會讓收集器更加積極,還會增長每一個增量步驟的長度。 不要把這個值設得小於 100 , 那樣的話收集器就工做的太慢了以致於永遠都幹不完一個循環。 默認值是 200 ,這表示收集器之內存分配的"兩倍"速工做。
若是你把步進倍率設爲一個很是大的數字 (比你的程序可能用到的字節數還大 10% ), 收集器的行爲就像一個 stop-the-world 收集器。 接着你若把間歇率設爲 200 , 收集器的行爲就和過去的 Lua 版本同樣了: 每次 Lua 使用的內存翻倍時,就作一次完整的收集。
Lua 提供瞭如下函數collectgarbage ([opt [, arg]])用來控制自動內存管理:
如下演示了一個簡單的垃圾回收實例:
mytable = {"apple", "orange", "banana"} print(collectgarbage("count")) mytable = nil print(collectgarbage("count")) print(collectgarbage("collect")) print(collectgarbage("count"))
執行以上程序,輸出結果以下(注意內存使用的變化):
20.9560546875 20.9853515625 0 19.4111328125
由 sf0501 建立, 最後一次修改 2015-09-24
面向對象編程(Object Oriented Programming,OOP)是一種很是流行的計算機編程架構。
如下幾種編程語言都支持面向對象編程:
咱們知道,對象由屬性和方法組成。LUA中最基本的結構是table,因此須要用table來描述對象的屬性。
lua中的function能夠用來表示方法。那麼LUA中的類能夠經過table + function模擬出來。
至於繼承,能夠經過metetable模擬出來(不推薦用,只模擬最基本的對象大部分時間夠用了)。
Lua中的表不只在某種意義上是一種對象。像對象同樣,表也有狀態(成員變量);也有與對象的值獨立的本性,特別是擁有兩個不一樣值的對象(table)表明兩個不一樣的對象;一個對象在不一樣的時候也能夠有不一樣的值,但他始終是一個對象;與對象相似,表的生命週期與其由什麼建立、在哪建立沒有關係。對象有他們的成員函數,表也有:
Account = {balance = 0} function Account.withdraw (v) Account.balance = Account.balance - v end
這個定義建立了一個新的函數,而且保存在Account對象的withdraw域內,下面咱們能夠這樣調用:
Account.withdraw(100.00)
如下簡單的類包含了三個屬性: area, length 和 breadth,printArea方法用於打印計算結果:
-- Meta class Rectangle = {area = 0, length = 0, breadth = 0} -- 派生類的方法 new function Rectangle:new (o,length,breadth) o = o or {} setmetatable(o, self) self.__index = self self.length = length or 0 self.breadth = breadth or 0 self.area = length*breadth; return o end -- 派生類的方法 printArea function Rectangle:printArea () print("矩形面積爲 ",self.area) end
建立對象是位類的實例分配內存的過程。每一個類都有屬於本身的內存並共享公共數據。
r = Rectangle:new(nil,10,20)
咱們能夠使用點號(.)來訪問類的屬性:
print(r.length)
咱們能夠使用冒號(:)來訪問類的屬性:
r:printArea()
內存在對象初始化時分配。
如下咱們演示了 Lua 面向對象的完整實例:
-- Meta class Shape = {area = 0} -- 基礎類方法 new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- 基礎類方法 printArea function Shape:printArea () print("面積爲 ",self.area) end -- 建立對象 myshape = Shape:new(nil,10) myshape:printArea()
執行以上程序,輸出結果爲:
面積爲 100
繼承是指一個對象直接使用另外一對象的屬性和方法。可用於擴展基礎類的屬性和方法。
如下演示了一個簡單的繼承實例:
-- Meta class Shape = {area = 0} -- 基礎類方法 new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- 基礎類方法 printArea function Shape:printArea () print("面積爲 ",self.area) end
接下來的實例,Square 對象繼承了 Shape 類:
Square = Shape:new() -- Derived class method new function Square:new (o,side) o = o or Shape:new(o,side) setmetatable(o, self) self.__index = self return o end
如下實例咱們繼承了一個簡單的類,來擴展派生類的方法,派生類中保留了繼承類的成員變量和方法:
-- Meta class Shape = {area = 0} -- 基礎類方法 new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- 基礎類方法 printArea function Shape:printArea () print("面積爲 ",self.area) end -- 建立對象 myshape = Shape:new(nil,10) myshape:printArea() Square = Shape:new() -- 派生類方法 new function Square:new (o,side) o = o or Shape:new(o,side) setmetatable(o, self) self.__index = self return o end -- 派生類方法 printArea function Square:printArea () print("正方形面積爲 ",self.area) end -- 建立對象 mysquare = Square:new(nil,10) mysquare:printArea() Rectangle = Shape:new() -- 派生類方法 new function Rectangle:new (o,length,breadth) o = o or Shape:new(o) setmetatable(o, self) self.__index = self self.area = length * breadth return o end -- 派生類方法 printArea function Rectangle:printArea () print("矩形面積爲 ",self.area) end -- 建立對象 myrectangle = Rectangle:new(nil,10,20) myrectangle:printArea()
執行以上代碼,輸出結果爲:
面積爲 100 正方形面積爲 100 矩形面積爲 200
Lua 中咱們能夠重寫基礎類的函數,在派生類中定義本身的實現方式:
-- 派生類方法 printArea function Square:printArea () print("正方形面積 ",self.area) end
由 sf0501 建立, 最後一次修改 2015-09-14
本文主要爲你們介紹 Lua 數據庫的操做庫:LuaSQL。他是開源的,支持的數據庫有:ODBC, ADO, Oracle, MySQL, SQLite 和 PostgreSQL。
本文爲你們介紹MySQL的數據庫鏈接。
LuaSQL 能夠使用 LuaRocks 來安裝能夠根據須要安裝你須要的數據庫驅動。
LuaRocks 安裝方法:
$ wget http://luarocks.org/releases/luarocks-2.2.1.tar.gz $ tar zxpf luarocks-2.2.1.tar.gz $ cd luarocks-2.2.1 $ ./configure; sudo make bootstrap $ sudo luarocks install luasocket $ lua Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio > require "socket"
Window 下安裝 LuaRocks:https://github.com/keplerproject/luarocks/wiki/Installation-instructions-for-Windows
安裝不一樣數據庫驅動:
luarocks install luasql-sqlite3 luarocks install luasql-postgres luarocks install luasql-mysql luarocks install luasql-sqlite luarocks install luasql-odbc
你也能夠使用源碼安裝方式,Lua Github 源碼地址:https://github.com/keplerproject/luasql
Lua 鏈接MySql 數據庫:
require "luasql.mysql" --建立環境對象 env = luasql.mysql() --鏈接數據庫 conn = env:connect("數據庫名","用戶名","密碼","IP地址",端口) --設置數據庫的編碼格式 conn:execute"SET NAMES UTF8" --執行數據庫操做 cur = conn:execute("select * from role") row = cur:fetch({},"a") --文件對象的建立 file = io.open("role.txt","w+"); while row do var = string.format("%d %s\n", row.id, row.name) print(var) file:write(var) row = cur:fetch(row,"a") end file:close() --關閉文件對象 conn:close() --關閉數據庫鏈接 env:close() --關閉數據庫環境
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
本文針對的讀者是有經驗的C/C++程序員,但願瞭解Lua或者迅速抓住Lua的關鍵概念和模式進行開發的。所以本文並不打算教給讀者條件語句的語法或者函數定義的方式等等顯而易見的東西,以及一些諸如變量、函數等編程語言的基本概念。本文只打算告訴讀者Lua那些與C/C++顯著不一樣的東西以及它們實際上帶來了怎樣不一樣於C/C++的思考方式。不要小看它們,它們即將顛覆你傳統的C/C++的世界觀!
本文一共分初階、進階和高階三大部分,每一個部分又有若干章節。讀者應當從頭到尾按部就班的閱讀,可是標有「*」號的章節(主要討論OO在Lua中的實現方式)能夠略去而不影響對後面內容的理解。讀者只要把前兩部分完成就能夠勝任Lua開發的絕大部分任務。高階部分可做爲選擇。
基本類型 | 描述 | 備註 |
---|---|---|
數值(number) | 內部以double表示 | |
字符串(string) | 老是以零結尾,但能夠包含任意字符(包括零),所以並不等價於C字符串, 而是其超集 | |
布爾(boolean) | 只有「true」或者「false」兩個值。 | |
函數(function) | Lua的關鍵概念之一。不簡單等同於C的函數或函數指針。 | |
表(table) | 異構的Hash表。Lua的關鍵概念之一。 | |
userdata | 用戶(非腳本用戶)定義的C數據結構。腳本用戶只能使用它,不能定義。 | |
線程(thread) | Lua協做線程(coroutine),與通常操做系統的搶佔式線程不同。 | |
nil | 表明什麼也沒有,能夠與C的NULL做類比,但它不是空指針。 |
2.1 實例代碼
function foo(a,b,c,...) local sum = a+b return sum,c --函數能夠返回多個值 end r1,r2 = foo(1,"123","hello")--平行賦值 print(r1,r2);
輸出結果:
124 hello
2.2 函數基本使用方法
用關鍵字function定義函數,以關鍵字end結束
用關鍵字local定義。若是沒有用local定義,即便在函數內部定義的變量也是全局變量!
return a, b, c, ...
a, b = c, d
前面的代碼定義了三個全局變量:foo、r1和r2
3.1 實現代碼
local a = {} local b = {x = 1,["hello,"] = "world!"} a["astring"] = "ni,hao!" a[1] = 100 a["a table"] = b for k,v in pairs(a) do print(k,"=>",v); end
輸出結果:
1=>100
astring=>ni,hao!
a table=>table: 0xfd59570
3.2 表使用方法
a = {}, b = {...}
經過「.」或者「[]」運算符來訪問表的成員。
注意:表達式a.b等價於a[「b」],但不等價於a[b]
表項的鍵和值
任何類型的變量,除了nil,均可以作爲表項的鍵。從簡單的數值、字符串到複雜的函數、表等等均可以;一樣,任何類型的變量,除了nil,均可以做爲表項的值。給一個表項的值賦nil意味着從表中刪除這一項,好比令a.b= nil,則把表a中鍵爲「b」的項刪除。若是訪問一個不存在的表項,其值也是nil,好比有c = a.b,但表a中沒有鍵爲「b」的項,則c等於nil。
4.1 實現代碼
function create(name,id) local obj = {name = name,id = id} function obj:SetName(name) self.name = name end function obj:GetName() return self.name end function obj:SetId(id) self.id = id end function obj:GetId() return self.id end return obj end local myCreate = create("sam",001) for k,v in pairs(myCreate) do print(k,"=>",v) end print("myCreate's name:",myCreate:GetName(),"myCreate's id:",myCreate.GetId(myCreate)) myCreate:SetId(100) myCreate:SetName("Hello Kity") print("myCreate's name:",myCreate:GetName(),"myCreate's id:",myCreate:GetId())
SetName=>function: 0x85efc50
GetId=>function: 0x85efc10
id=>1
SetId=>function: 0x85efd00
GetName=>function: 0x85efce0
name=>sam
myCreate's name:sammyCreate's id:1
myCreate's name:Hello KitymyCreate's id:100
4.2對象實現描述
如前面代碼的create函數
把對象的數據和方法都放在一張表內,雖然沒有隱藏私有成員,但對於簡單腳原本說徹底能夠接受。
成員方法的定義
function obj:method(a1, a2, ...) ... end 等價於function obj.method(self, a1, a2, ...) ... end 等價於obj.method = function (self, a1, a2, ...) ... end
成員方法的調用
obj:method(a1, a2, ...) 等價於obj.method(obj, a1, a2, ...)
5.1 實現代碼
local function CreateRobot(name,id) local obj = {name = name,id = id} function obj:SetName(name) self.name = name end function obj:GetName() return self.name end function obj:SetId(id) self.id = id end function obj:GetId() return self.id end return obj end local function createFootballRobot(name ,id ,position) local obj = CreateRobot(name ,id) obj.position = "right back" function obj:SetPosition(p) self.position = p end function obj:GetPosition() return self.position end return obj end local mycreateFootballRobot = createFootballRobot("Tom",1000,"廣州") print("mycreateFootballRobot's name:",mycreateFootballRobot:GetName(),"myCreate's id:",mycreateFootballRobot:GetId(),mycreateFootballRobot:GetPosition()) mycreateFootballRobot:SetName("麥迪") mycreateFootballRobot:SetId(2000) mycreateFootballRobot:SetPosition("北京") print("mycreateFootballRobot's name:",mycreateFootballRobot:GetName(),"myCreate's id:",mycreateFootballRobot:GetId(),mycreateFootballRobot:GetPosition())
輸出結果:
mycreateFootballRobot's name:TommyCreate's id:1000right back
mycreateFootballRobot's name:麥迪myCreate's id:2000北京
5.2 簡單繼承優缺點
優勢: 簡單、直觀
缺點: 傳統、不夠動態
參考文獻《C/C++程序員的Lua快速入門》
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
1.1 實例代碼
function createCountdownTimer(second) local ms = second * 1000 --ms爲countDown的Upvalue local function countDown() ms = ms -1 return ms end return countDown end local timer1 = createCountdownTimer(1) for i = 1, 3 do print(timer1()) end
輸出結果:
999
998
997
1.2 關於函數閉包描述
Upvalue
一個函數所使用的定義在它的函數體以外的局部變量(external local variable)稱爲這個函數的upvalue。 在前面的代碼中,函數countDown使用的定義在函數createCountdownTimer 中的局部變量ms就是countDown的upvalue,但ms對createCountdownTimer而 言只是一個局部變量,不是upvalue。 Upvalue是Lua不一樣於C/C++的特有屬性,須要結合代碼仔細體會。
函數閉包
一個函數和它所使用的全部upvalue構成了一個函數閉包。
Lua函數閉包使函數具備保持它本身的狀態的能力,從這個意義上說,能夠 與帶靜態局部變量的C函數相類比。但兩者有顯著的不一樣:對Lua來講,函數 是一種基本數據類型——表明一種(可執行)對象,能夠有本身的狀態;但 是對帶靜態局部變量的C函數來講,它並非C的一種數據類型,更不會產生 什麼對象實例,它只是一個靜態地址的符號名稱。
2.2 實例代碼
local function create(name ,id ) local data = {name = name ,id = id} --data爲obj.SetName,obj.GetName,obj.SetId,obj.GetId的Upvalue local obj = {} --把須要隱藏的成員放在一張表裏,把該表做爲成員函數的upvalue。 function obj.SetName(name) data.name = name end function obj.GetName() return data.name end function obj.SetId(id) data.id = id end function obj.GetId() return data.id end return obj end
輸出結果:
mycreate's id:1mycreate's name:Sam
mycreate's id:1mycreate's name:Lucy
2.2 有關對象實現的描述
實現方式: 把須要隱藏的成員放在一張表裏,把該表做爲成員函數的upvalue。
侷限性: 基於對象的實現不涉及繼承及多態。但另外一方面,腳本編程是否須要繼承和多態要視狀況而定。
3.1 實例代碼(1):
local t = {} local m = {a = "and",b = "Li Lei", c = "Han Meimei"} setmetatable(t,{__index = m}) --表{ __index=m }做爲表t的元表 for k,v in pairs(t) do --窮舉表t print("有值嗎?") print(k,"=>",v) end print("-------------") print(t.b, t.a, t.c)
輸出結果:
Li LeiandHan Meimei
3.2 實例代碼(2):
local function add(t1,t2) --‘#’運算符取表長度 assert(#t1 == #t2) local length = #t1 for i = 1,length do t1[i] = t1[i] + t2[i] end return t1 end --setmetatable返回被設置的表 t1 = setmetatable({1,2,3},{__add = add}) t2 = setmetatable({10,20,30},{__add = add}) for k,v in pairs(t1) do print(k,"=>",v) end for k,v in pairs(t2) do print(k,"=>",v) end print("---------兩元表相加--------------") t1 = t1 + t2 for i = 1 ,#t1 do print(t1[i]) end
輸出結果:
1=>1
2=>2
3=>3
1=>10
2=>20
3=>30
---------兩元表相加--------------
11
22
33
3.3 有關元表的描述:
定義 :
元表自己只是一個普通的表,經過特定的方法(好比setmetatable)設置到某個對象上,進而影響這個對象的行爲;一個對象有哪些行爲受到元表影響以及這些行爲按照何種方式受到影響是受Lua語言約束的。好比在前面的代碼裏,兩個表對象的加法運算,若是沒有元表的干預,就是一種錯誤;可是Lua規定了元表能夠「重載」對象的加法運算符,所以若把定義了加法運算的元表設置到那兩個表上,它們就能夠作加法了。元表是Lua最關鍵的概念之一,內容也很豐富,請參考Lua文檔瞭解詳情。
元表與C++虛表的比較:
若是把表比做對象,元表就是能夠改變對象行爲的「元」對象。在某種程度上,元表能夠與C++的虛表作一類比。但兩者仍是迥然不一樣的:元表能夠動態的改變,C++虛表是靜態不變的;元表能夠影響表(以及其餘類型的對象)的不少方面的行爲,虛表主要是爲了定位對象的虛方法(最多再帶上一點點RTTI)。
4.1 實例代碼
local Robot = { name = "Sam", id = 001 } function Robot:New(extension) local t = setmetatable(extension or { }, self) self.__index = self return t end function Robot:SetName(name) self.name = name end function Robot:GetName() return self.name end function Robot:SetId(id) self.id = id end function Robot:GetId() return self.id end robot = Robot:New() print("robot's name:", robot:GetName()) print("robot's id:", robot:GetId()) print("-----------------") local FootballRobot = Robot:New({position = "right back"}) function FootballRobot:SetPosition(p) self.position = p end function FootballRobot:GetPosition() return self.position end fr = FootballRobot:New() print("fr's position:", fr:GetPosition()) print("fr's name:", fr:GetName()) print("fr's id:", fr:GetId()) print("-----------------") fr:SetName("Bob") print("fr's name:", fr:GetName()) print("robot's name:", robot:GetName())
輸出結果:
robot's name:Sam
robot's id:1
fr's position:right back
fr's name:Sam
fr's id:1
fr's name:Bob
robot's name:Sam
4.2 相關描述:
prototype模式一個對象既是一個普通的對象,同時也能夠做爲建立其餘對象的原型的對象(即類對象,class object);動態的改變原型對象的屬性就能夠動態的影響全部基於此原型的對象;另外,基於一個原型被建立出來的對象能夠重載任何屬於這個原型對象的方法、屬性而不影響原型對象;同時,基於原型被建立出來的對象還能夠做爲原型來建立其餘對象。
5.1 實例代碼:
hello.lua
local pack = require "mypack" --導入包[注:包的名字與定義包的文件的名字相同(除去文件名後綴,在前面的代碼中,就是「mypack」)] print(ver or "No ver defined!") print(pack.ver) pack.aFunInMyPack() print(aFunInMyPack or "No aFunInMyPack defined!") aFuncFromMyPack()
mypack.lua
module(..., package.seeall) --定義包 ver = "0.1 alpha" function aFunInMyPack() print("Hello!") end _G.aFuncFromMyPack = aFunInMyPack
輸出結果:
No ver defined!
0.1 alpha
Hello!
No aFunInMyPack defined!
Hello!
5.2有關包的描述:
包是一種組織代碼的方式。
通常在一個Lua文件內以module函數開始定義一個包。module同時定義了一個新的包的函數環境,以使在此包中定義的全局變量都在這個環境中,而非使用包的函數的環境中。理解這一點很是關鍵。之前面的代碼爲例, 「module(..., package.seeall)」的意思是定義一個包,包的名字與定義包的文件的名字相同(除去文件名後綴,在前面的代碼中,就是「mypack」),而且在包的函數環境裏能夠訪問使用包的函數環境(好比,包的實現使用了print,這個變量沒有在包裏定義,而是定義在使用包的外部環境中)。
通常用require函數來導入一個包,要導入的包必須被置於包路徑(packagepath)上。包路徑能夠經過package.path或者環境變量來設定。通常來講,當前工做路徑老是在包路徑中。
請參考Lua手冊進一步瞭解包的詳細說明。
參考文獻《C/C++程序員的Lua快速入門》
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
1.1 實例代碼:
--迭代 local function enum(array) local index = 1 return function() local ret = array[index] index = index + 1 return ret end end local function foreach(array,action) for element in enum(array)do action(element) end end foreach({1,2,3},print)
輸出結果:
1
2
3
1.2 有關迭代的描述:
迭代是for語句的一種特殊形式,能夠經過for語句驅動迭代函數對一個給定集合進行遍歷。正式、完備的語法說明較複雜,請參考Lua手冊。
如前面代碼所示:enum函數返回一個匿名的迭代函數,for語句每次調用該迭代函數都獲得一個值(經過element變量引用),若該值爲nil,則for循環結束。
2.1 實例代碼
--線程 local function producer() return coroutine.create( function(salt) local t = {1,2,3} for i = 1,#t do salt = coroutine.yield(t[i] + salt) end end ) end function consumer(prod) local salt = 10 while true do local running ,product = coroutine.resume(prod, salt) salt = salt*salt if running then print(product or "END!") else break end end end consumer(producer())
輸出結果:
11
102
10003
END!
2.2 有關協做線程的描述:
經過coroutine.create能夠建立一個協做線程,該函數接收一個函數類型的參數做爲線程的執行體,返回一個線程對象。
經過coroutine.resume能夠啓動一個線程或者繼續一個掛起的線程。該函數接收一個線程對象以及其餘須要傳遞給該線程的參數。線程能夠經過線程函數的參數或者coroutine.yield調用的返回值來獲取這些參數。當線程初次執行時,resume傳遞的參數經過線程函數的參數傳遞給線程,線程從線程函數開始執行;當線程由掛起轉爲執行時,resume傳遞的參數以yield調用返回值的形式傳遞給線程,線程從yield調用後繼續執行
線程放棄調度
線程調用coroutine.yield暫停本身的執行,並把執行權返回給啓動/繼續它的線程;線程還可利用yield返回一些值給後者,這些值以resume調用的返回值的形式返回。
lua 論壇(lua 中國開發者 luaer中國官司方網站)
Lua參考手冊(最正式、權威的Lua文檔)
Lua編程(在線版,一樣具權威性的Lua教科書)
Lua正式網站的文檔頁面(包含不少有價值的文檔資料連接)
Lua維基(最全面的Lua維基百科)
LuaForge(最豐富的Lua開源代碼基地)
參考文獻《C/C++程序員的Lua快速入門》
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
表1
基本函數庫 | 功能 | 參數 | 備註 |
---|---|---|---|
assert(v[,mess age]) | 至關於C的斷言 | v:當表達式v爲nil或false將觸發錯誤,message:發生錯誤時返回的信息,默認爲"assertion failed!" | |
collectgarbage (opt [, arg]) | 是垃圾收集器的通用接口,用於操做垃圾收集器 | opt:操做方法標誌"Stop": 中止垃圾收集器 "Restart": 重啓垃圾收集器 "Collect": 執行一次全垃圾收集循環"Count": 返回當前Lua中使用的內存量(以KB爲單位)"Step": 單步執行一個垃圾收集. 步長 "Size" 由參數arg指定 (大型的值須要多步才能完成),若是要準確指定步長,須要屢次實驗以達最優效果。若是步長完成一次收集循環,將返回True"Setpause": 設置 arg/100 的值做爲暫定收集的時長 "Setstepmul": 設置 arg/100 的值,做爲步長的增幅(即新步長=舊步長*arg/100) | |
dofile (filename) | 打開而且執行一個lua塊,當忽略參數filename時,將執行標準輸入設備(stdin)的內容。返回全部塊的返回值。當發生錯誤時,dofile將錯誤反射給調用者 | 注:dofile不能在保護模式下運行 | |
error (message [, level]) | 終止正在執行的函數,並返回message的內容做爲錯誤信息(error函數永遠都不會返回) | 一般狀況下,error會附加一些錯誤位置的信息到message頭部.Level參數指示得到錯誤的位置,Level=1[默認]:爲調用error位置(文件+行號)Level=2:指出哪一個調用error的函數的函數Level=0:不添加錯誤位置信息 | |
_G全局環境表(全局變量) | 記錄全局環境的變量值的表 _G._G = _G | ||
getfenv(f) | 返回函數f的當前環境表 | f能夠爲函數或調用棧的級別,級別1[默認]爲當前的函數,級別0或其它值將返回全局環境_G | |
getmetatable(object) | 返回指定對象的元表(若object的元表.__metatable項有值,則返回object的元表.__metatable的值),當object沒有元表時將返回nil | ||
ipairs (t) | 返回三個值 迭代函數、表、0多用於窮舉表的鍵名和鍵值對如:for i,v in ipairs(t) do end每次循環將索引賦級i,鍵值賦給v | 注:本函數只能用於以數字索引訪問的表如:t={"1","cash"} | |
load (func [, chunkname]) | 裝載一個塊中的函數,每次調用func將返回一個鏈接前一結的字串,在塊結尾處將返回nil當沒有發生錯誤時,將返回一個編譯完成的塊做爲函數,不然返回nil加上錯誤信息,此函數的環境爲全局環境chunkname用於錯誤和調試信息 | ||
loadfile ([filename]) | 與load相似,但裝載的是文件或當沒有指定filename時裝載標準輸入(stdin)的內容 | ||
loadstring (string [, chunkname]) | 與load相似,但裝載的內容是一個字串如:assert(loadstring(s))() | ||
next (table [, index]) | 容許程序遍歷表中的每個字段,返回下一索引和該索引的值。 | table:要遍歷的表index:要返回的索引的前一索中的號,當index爲nil[]時,將返回第一個索引的值,當索引號爲最後一個索引或表爲空時將返回nil注:能夠用next(t)來檢測表是否爲空(此函數只能用於以數字索引的表與ipairs相相似) | |
ipairs (t) | 返回三個值 next函數、表、0多用於窮舉表的鍵名和鍵值對如:for n,v in pairs(t) do end | 每次循環將索引賦級i,鍵值賦給v注:本函數只能用於以鍵名索引訪問的表如:t={id="1",name="cash"} | |
pcall (f, arg1, ···) | 在保護模式下調用函數(即發生的錯誤將不會反射給調用者)當調用函數成功能返回true,失敗時將返回false加錯誤信息 | ||
print (···) | 簡單的以tostring方式格式化輸出參數的內容 | ||
rawequal (v1, v2) | 檢測v1是否等於v2,此函數不會調用任何元表的方法 | ||
rawget (table, index) | 獲取表中指定索引的值,此函數不會調用任何元表的方法,成功返回相應的值,當索引不存在時返回nil | 注:本函數只能用於以數字索引訪問的表如:t={"1","cash"} | |
rawset (table, index, value) | 設置表中指定索引的值,此函數不會調用任何元表的方法,此函數將返回table | ||
select (index, ···) | 當index爲數字將返回全部index大於index的參數:如:select(2,"a","b") 返回 "b"當index爲"#",則返回參數的總個數(不包括index) | ||
setfenv (f, table) | 設置函數f的環境表爲table | f能夠爲函數或調用棧的級別,級別1[默認]爲當前的函數,級別0將設置當前線程的環境表 | |
setmetatable (table, metatable) | 指定的table設置元表metatable,若是metatable爲nil則取消table的元表,當metatable有__metatable字段時,將觸發錯誤 | 注:只能爲LUA_TTABLE表類型指定元表 | |
tonumber (e [, base]) | 嘗試將參數e轉換爲數字,當不能轉換時返回nil | base(2~36)指出參數e當前使用的進制,默認爲10進制,如tonumber(11,2)=3 | |
tostirng(e) | 將參數e轉換爲字符串,此函數將會觸發元表的__tostring事件 | ||
type(v) | 返回參數的類型名("nil","number", "string", "boolean", "table", "function", "thread", "userdata") | ||
unpack (list [, i [, j]]) | 返回指定表的索引的值,i爲起始索引,j爲結束索引 | 注:本函數只能用於以數字索引訪問的表,不然只會返回nil如:t={"1","cash"} | |
_VERSION | 返回當前Lua的版本號"Lua 5.1". | ||
xpcall (f, err) | 與pcall相似,在保護模式下調用函數(即發生的錯誤將不會反射給調用者)但可指定一個新的錯誤處理函數句柄當調用函數成功能返回true,失敗時將返回false加err返回的結果 |
引用博客:http://www.cnblogs.com/whiteyun/archive/2009/08/12/1543184.html
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
Lua5.1中數學庫的全部函數以下表:
math.pi 爲圓周率常量 = 3.14159265358979323846
表1
數學庫 | 說明 | 例子 | 方法 |
---|---|---|---|
abs | 取絕對值 | math.abs(-15) | 15 |
acos | 反餘弦函數 | math.acos(0.5) | 1.04719755 |
asin | 反正弦函數 | math.asin(0.5) | 0.52359877 |
atan2 | x / y的反正切值 | math.atan2(90.0, 45.0) | 1.10714871 |
atan | 反正切函數 | math.atan(0.5) | 0.463647609 |
ceil | 不小於x的最大整數 | math.ceil(5.8) | 6 |
cosh | 雙曲線餘弦函數 | math.cosh(0.5) | 1.276259652 |
cos | 餘弦函數 | math.cos(0.5) | 0.87758256 |
deg | 弧度轉角度 | math.deg(math.pi) | 180 |
exp | 計算以e爲底x次方值 | math.exp(2) | 2.718281828 |
floor | 不大於x的最大整數 | math.floor(5.6) | 5 |
fmod (mod) | 取模運算 | math.mod(14, 5) | 4 |
frexp | 把雙精度數val分解爲數字部分(尾數)和以2爲底的指數n,即val=x*2n | math.frexp(10.0) | 0.625 4 |
ldexp | 計算value * 2的n次方 | math.ldexp(10.0, 3) | 80 = 10 * (2 ^3) |
log10 | 計算以10爲基數的對數 | math.log10(100) | 2 |
log | 計算一個數字的天然對數 | math.log(2.71) | 0.9969 |
max | 取得參數中最大值 | math.max(2.71, 100, -98, 23) | 100 |
min | 取得參數中最小值 | math.min(2.71, 100, -98, 23) | -98 |
modf | 把數分爲整數和小數 | math.modf(15.98) | 15 98 |
pow | 獲得x的y次方 | math.pow(2, 5) | 32 |
rad | 角度轉弧度 | math.rad(180) | 3.14159265358 |
random | 獲取隨機數 | math.random(1, 100) math.random(100) | 獲取1-100的隨機數 |
randomseed | 設置隨機數種子 | math.randomseed(os.time()) | 在使用math.random函數以前必須使用此函數設置隨機數種子 |
sinh | 雙曲線正弦函數 | math.sinh(0.5) | 0.5210953 |
sin | 正弦函數 | math.sin(math.rad(30)) | 0.5 |
sqrt | 開平方函數 | math.sqrt(16) | 4 |
tanh | 雙曲線正切函數 | math.tanh(0.5) | 0.46211715 |
tan | 正切函數 | math.tan(0.5) | 0.5463024 |
引用博客:http://www.cnblogs.com/whiteyun/archive/2009/08/10/1543040.html
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
爲了讓遊戲前端數據輸出更加條理,作了一個簡單樹狀結構來打印數據。
ccmlog.lua
local function __tostring(value, indent, vmap) local str = '' indent = indent or '' vmap = vmap or {} --遞歸結束條件 if (type(value) ~= 'table') then if (type(value) == 'string') then --字符串 str = string.format("[[%s]]", value) else --整數 str = tostring(value) end else if type(vmap) == 'table' then if vmap[value] then return '('..tostring(value)..')' end vmap[value] = true end local auxTable = {} --保存元表KEY(非整數) local iauxTable = {} --保存元表value local iiauxTable = {} --保存數組(key爲0) table.foreach(value, function(i, v) if type(i) == 'number' then if i == 0 then table.insert(iiauxTable, i) else table.insert(iauxTable, i) end elseif type(i) ~= 'table' then table.insert(auxTable, i) end end) table.sort(iauxTable) str = str..'{\n' local separator = "" local entry = "\n" local barray = true local kk,vv table.foreachi (iauxTable, function (i, k) if i == k and barray then entry = __tostring(value[k], indent..' \t', vmap) str = str..separator..indent..' \t'..entry separator = ", \n" else barray = false table.insert(iiauxTable, k) end end) table.sort(iiauxTable) table.foreachi (iiauxTable, function (i, fieldName) kk = tostring(fieldName) if type(fieldName) == "number" then kk = '['..kk.."]" end entry = kk .. " = " .. __tostring(value[fieldName],indent..' \t',vmap) str = str..separator..indent..' \t'..entry separator = ", \n" end) table.sort(auxTable) table.foreachi (auxTable, function (i, fieldName) kk = tostring(fieldName) if type(fieldName) == "number" then kk = '['..kk.."]" end vv = value[fieldName] entry = kk .. " = " .. __tostring(value[fieldName],indent..' \t',vmap) str = str..separator..indent..' \t'..entry separator = ", \n" end) str = str..'\n'..indent..'}' end return str end ccmlog = function(m,fmt,...) local args = {...} for k,arg in ipairs(args) do if type(arg) == 'table' or type(arg) == 'boolean' or type(arg) == 'function' or type(arg) == 'userdata' then args[k] = __tostring(arg) end end args[#args+1] = "nil" args[#args+1] = "nil" args[#args+1] = "nil" local str = string.format("[%s]:"..fmt.." %s", m, unpack(args)) print(str) local off = 1 local p = CCLOGWARN if m == 'error' then p = CCLOGERROR elseif m == 'warn' then p = CCLOGWARN end while off <= #str do local subStr = string.sub(str, off, off+1024) off = off + #subStr --p(subStr) end end --打印測試 reserved = { [100] = { 300, 400}, 200, { 300, 500}, abc = "abc",[0] = {1,2,3,"abc"}} ccmlog("d","d",reserved)
打印效果以下:
[d]:d { 200, { 300, 500 }, [0] = { 1, 2, 3, [[abc]] }, [100] = { 300, 400 }, abc = [[abc]] }
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
Lua中每一個值均可具備元表。 元表是普通的Lua表,定義了原始值在某些特定操做下的行爲。你可經過在值的原表中設置特定的字段來改變做用於該值的操做的某些行爲特徵。
例如,當數字值做爲加法的操做數時,Lua檢查其元表中的"__add"字段是否有個函數。若是有,Lua調用它執行加法。
咱們稱元表中的鍵爲事件(event),稱值爲元方法(metamethod)。前述例子中的事件是"add",元方法是執行加法的函數。
可經過函數getmetatable查詢任何值的元表。
在table中,我能夠從新定義的元方法有如下幾個:
__add(a, b) --加法 __sub(a, b) --減法 __mul(a, b) --乘法 __div(a, b) --除法 __mod(a, b) --取模 __pow(a, b) --乘冪 __unm(a) --相反數 __concat(a, b) --鏈接 __len(a) --長度 __eq(a, b) --相等 __lt(a, b) --小於 __le(a, b) --小於等於 __index(a, b) --索引查詢 __newindex(a, b, c) --索引更新(PS:不懂的話,後面會有講) __call(a, ...) --執行方法調用 __tostring(a) --字符串輸出 __metatable --保護元表
Lua中的每個表都有其Metatable。Lua默認建立一個不帶metatable的新表
t = {} print(getmetatable(t)) --> nil
能夠使用setmetatable函數設置或者改變一個表的metatable
t1 = {} setmetatable(t, t1) assert(getmetatable(t) == t1)
任何一個表均可以是其餘一個表的metatable,一組相關的表能夠共享一個metatable(描述他們共同的行爲)。一個表也能夠是自身的metatable(描述其私有行爲)。
接下來就介紹介紹若是去從新定義這些方法。
如今我使用完整的實例代碼來詳細的說明算術類元方法的使用。
Set = {} local mt = {} -- 集合的元表 -- 根據參數列表中的值建立一個新的集合 function Set.new(l) local set = {} setmetatable(set, mt) for _, v in pairs(l) do set[v] = true end return set end -- 並集操做 function Set.union(a, b) local retSet = Set.new{} -- 此處至關於Set.new({}) for v in pairs(a) do retSet[v] = true end for v in pairs(b) do retSet[v] = true end return retSet end -- 交集操做 function Set.intersection(a, b) local retSet = Set.new{} for v in pairs(a) do retSet[v] = b[v] end return retSet end -- 打印集合的操做 function Set.toString(set) local tb = {} for e in pairs(set) do tb[#tb + 1] = e end return "{" .. table.concat(tb, ", ") .. "}" end function Set.print(s) print(Set.toString(s)) end
如今,我定義「+」來計算兩個集合的並集,那麼就須要讓全部用於表示集合的table共享一個元表,而且在該元表中定義如何執行一個加法操做。首先建立一個常規的table,準備用做集合的元表,而後修改Set.new函數,在每次建立集合的時候,都爲新的集合設置一個元表。代碼以下:
Set = {} local mt = {} -- 集合的元表 -- 根據參數列表中的值建立一個新的集合 function Set.new(l) local set = {} setmetatable(set, mt) for _, v in pairs(l) do set[v] = true end return set end
在此以後,全部由Set.new建立的集合都具備一個相同的元表,例如:
local set1 = Set.new({10, 20, 30}) local set2 = Set.new({1, 2}) print(getmetatable(set1)) print(getmetatable(set2)) assert(getmetatable(set1) == getmetatable(set2))
最後,咱們須要把元方法加入元表中,代碼以下:
mt.__add = Set.union
這之後,只要咱們使用「+」符號求兩個集合的並集,它就會自動的調用Set.union函數,並將兩個操做數做爲參數傳入。好比如下代碼:
local set1 = Set.new({10, 20, 30}) local set2 = Set.new({1, 2}) local set3 = set1 + set2 Set.print(set3)
在上面列舉的那些能夠重定義的元方法均可以使用上面的方法進行重定義。如今就出現了一個新的問題,set1和set2都有元表,那咱們要用誰的元表啊?雖然咱們這裏的示例代碼使用的都是一個元表,可是實際coding中,會遇到我這裏說的問題,對於這種問題,Lua是按照如下步驟進行解決的:
以上就是Lua處理這個問題的規則,那麼咱們在實際編程中該如何作呢?
好比set3 = set1 + 8這樣的代碼,就會打印出如下的錯誤提示:
lua: test.lua:16: bad argument #1 to 'pairs' (table expected, got number)
可是,咱們在實際編碼中,能夠按照如下方法,彈出咱們定義的錯誤消息,代碼以下:
function Set.union(a, b) if getmetatable(a) ~= mt or getmetatable(b) ~= mt then error("metatable error.") end local retSet = Set.new{} -- 此處至關於Set.new({}) for v in pairs(a) do retSet[v] = true end for v in pairs(b) do retSet[v] = true end return retSet end
當兩個操做數的元表不是同一個元表時,就表示兩者進行並集操做時就會出現問題,那麼咱們就能夠打印出咱們須要的錯誤消息。
上面總結了算術類的元方法的定義,關係類的元方法和算術類的元方法的定義是相似的,這裏不作累述。
寫過Java或者C#的人都知道,Object類中都有一個tostring的方法,程序員能夠重寫該方法,以實現本身的需求。在Lua中,也是這樣的,當咱們直接print(a)(a是一個table)時,是不能夠的。那怎麼辦,這個時候,咱們就須要本身從新定義tostring元方法,讓print能夠格式化打印出table類型的數據。函數print老是調用tostring來進行格式化輸出,當格式化任意值時,tostring會檢查該值是否有一個tostring的元方法,若是有這個元方法,tostring就用該值做爲參數來調用這個元方法,剩下實際的格式化操做就由__tostring元方法引用的函數去完成,該函數最終返回一個格式化完成的字符串。例如如下代碼:
mt.__tostring = Set.toString
咱們會發現,使用getmetatable就能夠很輕易的獲得元表,使用setmetatable就能夠很容易的修改元表,那這樣作的風險是否是太大了,那麼如何保護咱們的元表不被篡改呢?在Lua中,函數setmetatable和getmetatable函數會用到元表中的一個字段,用於保護元表,該字段是metatable。當咱們想要保護集合的元表,是用戶既不能看也不能修改集合的元表,那麼就須要使用metatable字段了;當設置了該字段時,getmetatable就會返回這個字段的值,而setmetatable則會引起一個錯誤;如如下演示代碼:
function Set.new(l) local set = {} setmetatable(set, mt) for _, v in pairs(l) do set[v] = true end mt.__metatable = "You cannot get the metatable" -- 設置完個人元表之後,不讓其餘人再設置 return set end local tb = Set.new({1, 2}) print(tb) print(getmetatable(tb)) setmetatable(tb, {})
上述代碼就會打印如下內容:
{1, 2} You cannot get the metatable lua: test.lua:56: cannot change a protected metatable
是否還記得當咱們訪問一個table中不存在的字段時,會返回什麼值?默認狀況下,當咱們訪問一個table中不存在的字段時,獲得的結果是nil。可是這種情況很容易被改變;Lua是按照如下的步驟決定是返回nil仍是其它值得:
下面經過一個實際的例子來講明__index的使用。假設要建立一些描述窗口,每一個table中都必須描述一些窗口參數,例如顏色,位置和大小等,這些參數都是有默認值得,所以,咱們在建立窗口對象時能夠指定那些不一樣於默認值得參數。
Windows = {} -- 建立一個命名空間 -- 建立默認值表 Windows.default = {x = 0, y = 0, width = 100, height = 100, color = {r = 255, g = 255, b = 255}} Windows.mt = {} -- 建立元表 -- 聲明構造函數 function Windows.new(o) setmetatable(o, Windows.mt) return o end -- 定義__index元方法 Windows.mt.__index = function (table, key) return Windows.default[key] end local win = Windows.new({x = 10, y = 10}) print(win.x) -- >10 訪問自身已經擁有的值 print(win.width) -- >100 訪問default表中的值 print(win.color.r) -- >255 訪問default表中的值
根據上面代碼的輸出,結合上面說的那三步,咱們再來看看,print(win.x)時,因爲win變量自己就擁有x字段,因此就直接打印了其自身擁有的字段的值;print(win.width),因爲win變量自己沒有width字段,那麼就去查找是否擁有元表,元表中是否有index對應的元方法,因爲存在index元方法,返回了default表中的width字段的值,print(win.color.r)也是一樣的道理。
在實際編程中,__index元方法沒必要必定是一個函數,它還能夠是一個table。當它是一個函數時,Lua以table和不存在key做爲參數來調用該函數,這就和上面的代碼同樣;當它是一個table時,Lua就以相同的方式來從新訪問這個table,因此上面的代碼也能夠是這樣的:
-- 定義__index元方法 Windows.mt.__index = Windows.default
newindex元方法與index相似,newindex用於更新table中的數據,而index用於查詢table中的數據。當對一個table中不存在的索引賦值時,在Lua中是按照如下步驟進行的:
Lua解釋器先判斷這個table是否有元表;
那麼這裏就出現了一個問題,看如下代碼:
local tb1 = {} local tb2 = {} tb1.__newindex = tb2 tb2.__newindex = tb1 setmetatable(tb1, tb2) setmetatable(tb2, tb1) tb1.x = 10
發現什麼問題了麼?是否是循環了,在Lua解釋器中,對這個問題,就會彈出錯誤消息,錯誤消息以下:
loop in settable
引用博客:http://www.jellythink.com/archives/511
由 sf0501 建立, 最後一次修改 2016-11-16
table庫由一些操做table的輔助函數組成。他的主要做用之一是對Lua中array的大小給出一個合理的解釋。另外還提供了一些從list中插入刪除元素的函數,以及對array元素排序函數。
concat是concatenate(連鎖, 鏈接)的縮寫. table.concat()函數列出參數中指定table的數組部分從start位置到end位置的全部元素, 元素間以指定的分隔符(sep)隔開。除了table外, 其餘的參數都不是必須的, 分隔符的默認值是空字符, start的默認值是1, end的默認值是數組部分的總長.
sep, start, end這三個參數是順序讀入的, 因此雖然它們都不是必須參數, 但若是要指定靠後的參數, 必須同時指定前面的參數.
Lua 函數回調技巧 由 sf0501 建立,小路依依 最後一次修改 2016-11-16 技巧1: local a = {};function b() print("Hello World")enda["sell"] = {callFunc =b}a["sell"].callFunc() 技巧2: 使用lua 自帶的 unpack : 解釋:把一直數組(只有連續數字下標的 table)展開成一串返回值,可是對用字符串或別的東西作 key 的 table 無能爲力。 function unpackex(tbl, args) local ret = {} for _,v in ipairs(args) do table.insert(ret, tbl[v]) end return unpack(ret)endprint(unpackex({one = {"one", "two", "three"}, two = "T" , three = "TH"},{"one", "two", "three"})) 輸出:>> table: 00ABC2D0TTHtest = {"Tom", "Mary", "Jam","Hey"} print(table.concat(test, ":")) print("*************") print(table.concat(test, nil, 1, 2)) print("*************") print(table.concat(test, "\n", 2, 3)) print(table.maxn(test))
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
功能:按table的內容返回一個時間值(數字),若不帶參數則返回當前時間.(在許多系統中該數值是當前距離某個特定時間的秒數。)
說明:當爲函數調用附加一個特殊的時間表時,該函數就是返回距該表描述的時間的數值。這樣的時間表有以下的區間:
前三項是必需的,若是未定義後幾項,默認時間爲正午(12:00:00)。若是是在里約熱內盧(格林威治向西三個時區)的一臺Unix計算機上(相對時間爲1970年1月1日,00:00:00),對於pc機(中國時區而言)有稍微更改,更改了爲1970年1月1日,08:00:00,這是因我國與其它國家時間差致使。
例子:
print(os.time{year=1970, month=1, day=1,hour=8}) print(os.time{year=1970, month=1, day=1}) --若未定義「時,分,秒」,默認時間爲正午(04:00:00)
運行結果:
-->0
-->14400(14400 = 46060 )
功能:返回一個按format格式化日期、時間的字串或表
說明:函數date,實際上是time函數的一種「反函數」。它將一個表示日期和時間的數值,轉換成更高級的表現形式。其第一個參數是一個格式化字符串,描述了要返回的時間形式。第二個參數就是時間的數字表示,默認爲當前的時間。
參數:format:
*t":將返一個帶year(4位),month(1-12), day (1--31), hour (0-23), min (0-59), sec (0-61), wday (星期幾, 星期天爲1), yday (年內天數), and isdst (是否爲日光節約時間true/false)的帶鍵名的表;
若沒有"*t"則返回一個按C的strftime函數格式化的字符串;
若不帶參數,則按當前系統的設置返回格式化的字符串 os.date() <=> os.date("%c")
例子:我當前PC時間,如圖:
代碼:
t = os.date("*t", os.time()); for i, v in pairs(t) do print(i,"->",v); end
運行結果 :
運行結果和以上時鐘的秒,不一致,你想,截圖也要時間的,呵呵。
若是使用帶標記(見下表)的特殊字符串,os.data函數會將相應的標記位以時間信息進行填充,獲得一個包含時間的字符串。
例子:
print(os.date("today is %A, in %B")) print(os.date("%X", 906000490))
運行結果:
同時,也能夠使用明確的字符串格式方式(例如"%m/%d/%Y")
例子:
print(os.date("%m/%d/%Y", 906000490))
運行結果:
<img
src="/attachments/image/cimg/ph2os.difftime (t2, t1)/h2p功能:返回t1到t2相差的秒數
/pp例子:/pprecodet1 = os.time();for i = 0, 100000 do os.time();endt2 =
os.time();print(string.format(" t1:="" %d="" t2:="" %d",t1,t2))=""
print(os.date("%x",="" t1))="" t2))="" print(os.difftime(t2,="" t1));=""
code="" pre="" p運行結果:="" p="" pimg="" elapsed="" time="" :=""
%.2f\n",="" os.clock()="" -="" x));<="">
運行結果:
由 sf0501 建立, 最後一次修改 2016-11-16
項目要求對lua腳本進行加密,查了一下相關的資料 ,得知lua自己能夠使用luac將腳本編譯爲字節碼(bytecode)從而實現加密,試了一下,確實可行。下面是使用原生的lua解釋器編譯字節碼:
一、新建一個名爲1.lua的文件,裏面只有一句話print("Hello Lua"),新建一個空的out.lua腳本文件
二、開始--運行--cmd三、luac -o out.lua 1.lua
注: luac -o [編譯後腳本名] [腳本名],必要時帶上腳本路徑,如:
[編譯後腳本名] [腳本名],必要時帶上腳本路徑
回車以後,再打開out.lua就能夠看到編譯好的字節碼了,如:
而後實驗一下,執行這個字節碼腳本,能夠看到lua原生的解釋器能夠直接解析luac編譯出來的bytecode腳本,很方便!
重點:作完了以上的一系列以後,我照着這個方法編譯項目中的腳本,而後在cocos2dx環境下使用,發現不行!因而又查了一下資料,發現2dx使用的是luajit,lua原生編譯出來的bytecode和luajit是不兼容的,因此照着上面方法編譯出來的bytecode腳本沒法在2dx中使用。
解決這個問題其實很簡單,就是用2dx自帶的luajit編譯lua腳本,下面附上luajit編譯bytecode的方法:
一、在cocos2d-x-2.2.3\scripting\lua\luajit\LuaJIT-2.0.1\src目錄下有個msvcbuild.bat批處理文件,須要先把luajit.exe這個東西給編譯出來。
二、打開visual studio的命令行工具,這個只要裝了vs都會有,在安裝目錄裏面能夠找到。
三、用vs的命令行工具cd到luajit的src目錄
四、執行msvcbuild.bat批處理文件,編譯出luajit.exe
五、將生成的luajit.exe、lua51.dll、jit 複製到打包工具的相對目錄下,這樣在工具中就能夠直接調用luajit –b source_file out_file (通常都是lua後綴,代碼不用改動)
<img
src="/attachments/image/cimg/pp接下來就能夠使用luajit.exe編譯lua腳本的bytecode了:luajit
-b [腳本名] [編譯後的腳本名],執行完後會在src目錄下生成一個已經編譯成bytecode的jit.lua文件。/ppimg src="
attachments="" image="" cimg="" 2016-02-22_56cb2ca40043a.jpg"="" alt=""
p="" pimg="" jit.lua");運行結果爲:="" <="">
至此,luajit編譯bytecode加密已完成!
嚴重注意:例子中,我把編譯先後的腳本名字取的不同,是爲了讓你們看出差別化來,實際在項目中使用的時候,腳本的名字編譯先後最好都一致,否則在腳本中相互require的時候可能會出現問題!一個一個轉換腳太麻煩了,分享一個bat批處理,能夠批量轉換一個文件夾中的全部lua文件.
代碼以下:
@echo off if exist out rd /s /q out mkdir out :input cls set input=: set /p input= 拖入要編譯的lua文件夾: set "input=%input:"=%" if "%input%"==":" goto input if not exist "%input%" goto input for %%i in ("%input%") do if /i "%%~di"==%%i goto input pushd %cd% cd /d "%input%">nul 2>nul || exit set cur_dir=%cd% popd set /a num = 0 for /f "delims=" %%i in ('dir /b /a-d /s "%input%"') do (set /a num += 1 & luajit -b %%~fsi out/%%~nxi & echo %%~nxi) echo 編譯腳本數量:%num% ATTRIB out/*.* +R pause
編譯後,文件夾內全部的lua腳本將被批量編譯爲字節碼,並保存在xxx\out目錄下,如:
注:XXX爲打包加密文件路徑
還有小提示:ios64目前只支持lua,不支持用luajit生成二進制*.lua.
引用博客:http://jingyan.baidu.com/article/0a52e3f4179713bf62ed72f1.html
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
Lua解釋器對字符串的支持頗有限。一個程序能夠建立字符串並鏈接字符串,但不能截取子串,檢查字符串的大小,檢測字符串的內容。在Lua中操縱字符串的功能基原本自於string庫。
--返回字符串s的長度 local s = "HelloWorld" print(string.len(s)) -->10 --重複n次字符串s的串 print(string.rep(s,2)) -->HelloWorldHelloWorld --大寫字母轉換成小寫 print(string.lower(s)) -->helloworld --小寫轉換成大寫 print(string.upper(s)) -->HELLOWORLD --截取字符串 local s = "[in brackets]" print(string.sub(s,2,-1)) -->in brackets] --將每個數字轉換成字符 print(string.char(97)) -->a --將每個字符轉換成數字 print(string.byte("abc")) print(string.byte("abc", 2)) --> 98 print(string.byte("abc", -1)) --> 99 --注:使用負數索引訪問字符串的最後一個字符 --對字符串進行格式化輸出 PI = 3.14165120 print(string.format("pi = %.4f", PI)) -->pi = 3.1417 --註釋:使用和C語言的printf函數幾乎如出一轍,你徹底能夠照C語言的printf來使用這個函數.
注:
string庫中全部的字符索引從前日後是1,2,...;從後往前是-1,-2,...
string庫中全部的function都不會直接操做字符串,而是返回一個結果。
在string庫中功能最強大的函數是:string.find(字符串查找),string.gsub(全局字符串替換),and string.gfind(全局字符串查找)。這些函數都是基於模式匹配的。
1.string.find
說明:用來在目標串(subject string)內搜索匹配指定的模式的串。函數若是找到匹配的串返回他的位置,不然返回nil.最簡單的模式就是一個單詞,僅僅匹配單詞自己。好比,模式'hello'僅僅匹配目標串中的"hello"。當查找到模式的時候,函數返回兩個值:匹配串開始索引和結束索引。
local s = "hello world" i,j = string.find(s,"hello") print(i," ",j) -->1 5 print(string.find(s, "kity")) -->nil
string.find函數第三個參數是可選的:標示目標串中搜索的起始位置。當咱們想查找目標串中全部匹配的子串的時候,這個選項很是有用。
local s = "\nare you ok!\n OK\n" local t = {} local i = 0 while true do i = string.find(s,"\n",i+1) if i == nil then break end table.insert(t,i) end for k,v in pairs(t) do print(k,"->",v) end
運行結果:
1 -> 1 2 -> 13 3 -> 17
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
這篇文章主要介紹了Lua中的模塊(module)和包(package)詳解,本文講解了require函數、寫一個模塊、package.loaded、module函數等內容.
從Lua5.1版本開始,就對模塊和包添加了新的支持,但是使用require和module來定義和使用模塊和包。require用於使用模塊,module用於建立模塊。簡單的說,一個模塊就是一個程序庫,能夠經過require來加載。而後便獲得了一個全局變量,表示一個table。這個table就像是一個命名空間,其內容就是模塊中導出的全部東西,好比函數和常量,一個符合規範的模塊還應使require返回這個table。如今就來具體的總結一下require和module這兩個函數。如:
require "mod" mod.foo() local m2 = require "mod2" local f = mod2.foo f()
require函數的調用形式爲require "模塊名"。該調用會返回一個由模塊函數組成的table,而且還會定義一個包含該table的全局變量。在使用Lua中的標準庫時能夠不用顯示的調用require,由於Lua已經預先加載了他們。
require函數在搜素加載模塊時,有一套自定義的模式,如:
?;?.lua;c:/windows/?;/usr/local/lua/?/?.lua
在上面的模式中,只有問號(?)和分號(;)是模式字符,分別表示require函數的參數(模塊名)和模式間的分隔符。如:調用require "sql",將會打開如下的文件:
sql
sql.lua
c:/windows/sql
/usr/local/lua/sql/sql.lua
Lua將require搜索的模式字符串放在變量package.path中。當Lua啓動後,便以環境變量LUA_PATH的值來初始化這個變量。若是沒有找到該環境變量,則使用一個編譯時定義的默認路徑來初始化。若是require沒法找到與模塊名相符的Lua文件,就會找C程序庫。C程序庫的搜索模式存放在變量package.cpath中。而這個變量則是經過環境變量LUA_CPATH來初始化的。
新建一個文件,命名爲game.lua,代碼以下:
local M = {}; local modelName = ...; _G[modelName] = M; function M.play() print("那麼,開始吧"); end function M.quit() print("你走吧,我保證你不會出事的,呵,呵呵"); end return M;
加載game.lua,代碼以下:
game = require "test" game.play()
運行:
lua -e "io.stdout:setvbuf 'no'" "HelloWorld.lua"
那麼,開始吧
Exit code: 0
仔細閱讀上例中的代碼,咱們能夠發現一些細節上問題。好比模塊內函數之間的調用仍然要保留模塊名的限定符,若是是私有變量還須要加local關鍵字,同時不能加模塊名限定符。若是須要將私有改成公有,或者反之,都須要必定的修改。那又該如何規避這些問題呢?咱們能夠經過Lua的函數「全局環境」來有效的解決這些問題。
咱們把game.lua這個模塊裏的全局環境設置爲M,因而,咱們直接定義函數的時候,不須要再帶M前綴。
由於此時的全局環境就是M,不帶前綴去定義變量,就是全局變量,這時的全局變量是保存在M裏。
因此,實際上,play和quit函數仍然是在M這個table裏。
local M = {}; local modelName = ...; _G[modelName] = M; package.loaded[modname] = M setfenv(1, M); function play() print("那麼,開始吧"); end function quit() print("你走吧,我保證你不會出事的,呵,呵呵"); end return M;
在Lua 5.1中,咱們能夠用module(...)函數來代替如下代碼,如:
local modname = ... local M = {} _G[modname] = M package.loaded[modname] = M --[[ 和普通Lua程序塊同樣聲明外部函數。 --]] setfenv(1,M)
便是:
module(..., package.seeall); function play() print("那麼,開始吧") end function quit() print("你走吧,我保證你不會出事的,呵,呵呵"); end
因爲在默認狀況下,module不提供外部訪問,必須在調用它以前,爲須要訪問的外部函數或模塊聲明適當的局部變量。而後Lua提供了一種更爲方便的實現方式,即在調用module函數時,多傳入一個package.seeall的參數,至關於 setmetatable(M, {__index = _G}) .
如:
module(...,package.seeall)
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
I/O庫爲文件操做提供兩種模式。簡單模式(simple model)擁有一個當前輸入文件和一個當前輸出文件,而且提供針對這些文件相關的操做。徹底模式(complete model)使用外部的文件句柄來實現。
I/O庫將當前輸入文件做爲標準輸入(stdin),將當前輸出文件做爲標準輸出(stdout)。這樣當咱們執行io.read,就是在標準輸入中讀取一行。
寫操做較讀操做簡單,咱們先從寫操做入手。
下面這個例子裏函數io.write獲取任意數目的字符串參數,接着將它們寫到當前的輸出文件。
local t = io.write("sin (3) = ", math.sin(3), "\n") --> sin (3) = 0.1411200080598672 print("hello", "Lua"); print("Hi") -->hello Lua -->Hi
注:Write函數與print函數不一樣在於,write不附加任何額外的字符到輸出中去,例如製表符,換行符等等。還有write函數是使用當前輸出文件,而print始終使用標準輸出。另外print函數會自動調用參數的tostring方法,因此能夠顯示出表(tables)函數(functions)和nil。
read函數:從當前輸入文件讀取串,由它的參數控制讀取的內容:
例子:
--io.read 從標準輸入流中得到,默認設置下,就是你的屏幕輸入 t = io.read("*all") t = string.gsub(t, ...) -- do the job io.write(t) -- write the
提示:若使用luaEditor編輯器,估計沒法在屏幕輸入。
徹底模式的核心在於文件句柄(file handle)。該結構相似於C語言中的文件流(FILE*),其呈現了一個打開的文件以及當前存取位置。打開一個文件的函數是io.open。它模仿C語言中的fopen函數,一樣須要打開文件的文件名參數,打開模式的字符串參數:
例子:
--讀操做 file = io.open("testRead.txt", "r") for line in file:lines() do print(line) end file:close() --寫操做 file = io.open("testRead.txt","a+") file:write("\nhello") file:close()
素材:
內容:
運行結果:
文件內容:
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
lua編程中,常常遇到函數的定義和調用,有時候用點號調用,有時候用冒號調用,這裏簡單的說明一下原理。如:
-- 點號定義和點號調用: girl = {money = 200} function girl.goToMarket(girl ,someMoney) girl.money = girl.money - someMoney end girl.goToMarket(girl ,100) print(girl.money)
-- 參數self指向調用者自身(相似於c++裏的this 指向當前類) girl = {money = 200} function girl.goToMarket(self ,someMoney) self.money = self.money - someMoney end girl.goToMarket(girl, 100) print(girl.money)
-- 冒號定義和冒號調用: girl = {money = 200} function girl:goToMarket(someMoney) self.money = self.money - someMoney end girl:goToMarket(100) print(girl.money)
冒號定義和冒號調用其實跟上面的效果同樣,只是把第一個隱藏參數省略了,而該參數self指向調用者自身。
總結:冒號只是起了省略第一個參數self的做用,該self指向調用者自己,並無其餘特殊的地方。
引用博文:http://www.xuebuyuan.com/1613223.html
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
開發中,大量使用lua,暫時根據當前情況,總結相對而言較好的規範,在多人協做中能夠更好的開發、交流。
介紹
該文檔旨在爲使用lua編寫應用程序創建編碼指南。
制訂編碼規範的目的:
切記:善用調試器。
1.全部lua文件命名時使用小寫字母、下劃線
2.類名、變量名儘量使用有意義的英文,類名使用帕斯卡命名法,變量名使用駱駝式命名法
3.常量、消息號定義時用大寫,單詞間 _ 分割 eg:KIND_PET_FOOD
4.枚舉值定義時 加前綴 enum_
\5. 函數名使用駱駝式命名法
注:
駱駝式命名法:第一個單字以小寫字母開始;第二個單字的首字母大寫或每個單字的首字母都採用大寫字母
帕斯卡命名法:和駱駝命名法很像,只有一點區別,就是首字母要大寫。(單字之間不以空格斷開或鏈接號)
\1. 文件開頭加上此文件的功能、職責的簡要描述;
以下:
--
-- Author: Feng
-- Date: XXXX-XX-XX
-- 功能描述
每一個文件都加module 限定詞; 導入的模塊都加 local 限定詞;或者使用(module(..., package.seeall)),這樣便於進行熱更新
\2. 全部提供外部函數都加以下格式的註釋。
例如:
--此函數檢測是否能夠從A(oldx, oldy)點走到B點(newx, newy)
--@param oldx 當前所在點x
--@param oldy 當前所在點y
--@param newx 目標點x
--@param newy 目標點y
--@return 若能夠到達,返回true;不然返回false
function Object:checkBar(oldx, oldy, newx, newy)
…
end
\3. 函數與函數間、以及一些定義之間加上空行。
\4. 函數內的臨時變量、文件內的局部函數都加上 local 限定詞
\5. 函數的行數過長(大於100行)時,儘可能拆分爲多個子函數;函數中一些晦澀的部分,必定要加上註釋。
\6. 短小的註釋使用 --; 較長的註釋使用 --[[ ]]
\7. assert函數開銷不小,請慎用。
\8. Lua類設計時,用元表來實現oop。
不要直接增長函數成員,由於直接增長函數成員會致使內存增長而且在jit下執行效率和用元表方式無差別。
\9. 文件使用UTF8格式
\1. 使用空行
在下述狀況下使用單行的空白行來分隔:
1)在方法之間
2)在方法內部代碼的邏輯段落小節之間
3)在註釋行以前
註釋以前增長一行或者多行空行。
2.使用空格符
除正常的成分之間以空格符分隔名(如數據類型和變量名之間),在下述狀況下也應使用一個空格符來分隔:
1)運算符和運算符之間,如: c = a + b;
2)在參數列表中的逗號後面,如:
function m1(year, month)
end
3) 在for語句時,如:
for k, v in pairs(t) do
end
4)在下列狀況下不要使用空格。
例如:
函數定義時:
function test1(a)
end
不要這樣:
function test1( a )
end
函數調用時:
test1(3)
不要這樣:
test1( 3 )
不要如此的緣由在於:
a).容易忘記相關空格,致使風格不統一,這樣還不如不加;
b).lua解析語法時是採用空格等分割來解析的,某些狀況下,若不當心加空格會致使非預期的結果。
\3. 使用換行符
不建議在一行中寫多條語句,一條語句的長度通常超過了80個字符時,應該換行
\4. 使用小括號
能夠使用小括號來強行規定運算順序
\5. 使用縮進
在下述狀況下應用縮進
1)類中的成分
2)方法體或語句塊中的成分
3)換行時的非起始行
縮減量通常爲在上一級成分的基礎上跑到下一個製表位
1.代碼中使用的一些函數儘量在文件開頭或者當前局部環境中加local前綴從新定義下。
例如:
local assert = assert
2.儘可能減小表中的成員是另外一個表的引用。 考慮lua的垃圾收集機制、內存泄露等。
3.高級特性儘量不用
4.寫代碼時儘量寫的簡單,考慮性能時先作好推斷,看看能提高多少,增長的複雜度以及形成的代碼晦澀有多嚴重,而後再決定如何作
5.加載的xml數據表,儘量的作好數據校驗,若校驗失敗,要出發斷言,使服務器沒法啓動;不要等出錯時,回過頭來檢查是數據表問題仍是邏輯問題。
6.出錯時,記錄好錯誤日誌。
有的函數開銷比較大,而調用的頻率很低,那麼能夠不對他作優化;
反之,有的函數開銷較小,可是調用的頻率很高,從如何下降調用頻率以及減小函數開銷兩個角度去思考,而後定下優化方案
提交代碼以前,去掉或者註釋掉無關的代碼; 測試下保證服務器能夠正確啓動。
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
Lua能夠調用C函數的能力將極大的提升Lua的可擴展性和可用性。
對於有些和操做系統相關的功能,或者是對效率要求較高的模塊,咱們徹底能夠經過C函數來實現,以後再經過Lua調用指定的C函數。
對於那些可被Lua調用的C函數而言,其接口必須遵循Lua要求的形式,即typedef int (lua_CFunction)(lua_State L)。
簡單說明一下,該函數類型僅僅包含一個表示Lua環境的指針做爲其惟一的參數,實現者能夠經過該指針進一步獲取Lua代碼中實際傳入的參數。返回值是整型,表示該C函數將返回給Lua代碼的返回值數量,若是沒有返回值,則return 0便可。須要說明的是,C函數沒法直接將真正的返回值返回給Lua代碼,而是經過虛擬棧來傳遞Lua代碼和C函數之間的調用參數和返回值的。
實例代碼:
// testlua.cpp : 定義控制檯應用程序的入口點。 // #include "stdafx.h" #include <stdio.h> #include <string.h> #include <math.h> extern "C" { #include <lua.h> #include <lualib.h> #include <lauxlib.h> } //待Lua調用的C註冊函數 static int add2(lua_State* L) { //檢查棧中的參數是否合法,1表示Lua調用時的第一個參數(從左到右),依此類推。 //若是Lua代碼在調用時傳遞的參數不爲number,該函數將報錯並終止程序的執行。 double op1 = luaL_checknumber(L,1); double op2 = luaL_checknumber(L,2); //將函數的結果壓入棧中。若是有多個返回值,能夠在這裏屢次壓入棧中。 lua_pushnumber(L,op1 + op2); //返回值用於提示該C函數的返回值數量,即壓入棧中的返回值數量。 return 1; } //待Lua調用的C註冊函數。 static int sub2(lua_State* L) { double op1 = luaL_checknumber(L,1); double op2 = luaL_checknumber(L,2); lua_pushnumber(L,op1 - op2); return 1; } //待Lua調用的C註冊函數。 static int l_sin (lua_State *L) { double d = lua_tonumber(L, 1); /* get argument */ lua_pushnumber(L, sin(d)); /* push result */ return 1; /* number of results */ } int _tmain(int argc, _TCHAR* argv[]) { lua_State *L = luaL_newstate(); luaL_openlibs(L); //將指定的函數註冊爲Lua的全局函數變量,其中第一個字符串參數爲Lua代碼 //在調用C函數時使用的全局函數名,第二個參數爲實際C函數的指針。 lua_register(L, "add2", add2); lua_register(L, "sub2", sub2); lua_register(L, "l_sin", l_sin); //在註冊完全部的C函數以後,便可在Lua的代碼塊中使用這些已經註冊的C函數了。 luaL_dofile(L,"test.lua"); //if (luaL_dostring(L,testfunc)) // printf("Failed to invoke.\n"); //const char *buf = "print('Hello World')"; //luaL_dostring(L,buf); lua_close(L); return 0; }
test.lua
function show() print("helloworld") print(add2(1.0,2.0)) print(sub2(20.1,19)) print(l_sin(1)) end show()
運行結果:
引用博文:http://www.cnblogs.com/stephen-liu74/archive/2012/07/23/2469902.html
由 sf0501 建立,小路依依 最後一次修改 2016-11-16
Lua中的table不是一種簡單的數據結構,它能夠做爲其它數據結構的基礎。如數組、記錄、線性表、隊列和集合等,在Lua中均可以經過table來表示。
在lua中經過整數下標訪問表中的元素便可簡單的實現數組。而且數組沒必要事先指定大小,大小能夠隨須要動態的增加。
a = {} for i = 1,100 do a[i] = 0 end print("The length of array 'a' is " .. #a) squares = {1, 4, 9, 16, 25} print("The length of array 'a' is " .. #squares)
在Lua中習慣上數組的下表從1開始,Lua的標準庫與此習慣保持一致,所以若是你的數組下標也是從1開始你就能夠直接使用標準庫的函數,不然就沒法直接使用。
Lua中主要有兩種表示矩陣的方法,第一種是用數組的數組表示。也就是說一個表的元素是另外一個表。
local N = 3 local M = 3 mt = {} for i = 1,N do mt[i] = {} for j = 1,M do mt[i][j] = i * j end end mt = {} for i = 1, N do for j = 1, M do mt[(i - 1) * M + j] = i * j end end
Lua中用tables很容易實現鏈表,每個節點是一個table,指針是這個表的一個域,而且指向另外一個節點(table)。例如,要實現一個只有兩個域:值和指針的基本鏈表,代碼以下:
list = nil for i = 1, 10 do list = { next = list ,value = i} end local l = list while l do --print(l.value) l = l.next end
雖然能夠使用Lua的table庫提供的insert和remove操做來實現隊列,但這種方式實現的隊列針對大數據量時效率過低,有效的方式是使用兩個索引下標,一個表示第一個元素,另外一個表示最後一個元素。
List = {} --建立 function List.new() return {first = 0,last = -1} end --隊列頭插入 function List.pushFront(list,value) local first = list.first - 1 list.first = first list[first] = value end --隊列尾插入 function List.popFront(list) local first = list.first if first > list.last then error("List is empty") end local value = list[first] list[first] = nil list.first = first + 1 return value end function List.popBack(list) local last = list.last if list.first > last then error("List is empty") end local value = list[last] list[last] = nil list.last = last - 1 return value end --測試代碼 local testList = {first = 0,last = -1} local tableTest = 12 List.pushFront(testList,tableTest) print( List.popFront(testList))
簡單實現堆棧功能,代碼以下:
local stackMng = {} stackMng.__index = stackMng function stackMng:new() local temp = {} setmetatable(temp,stackMng) return temp end function stackMng:init() self.stackList = {} end function stackMng:reset() self:init() end function stackMng:clear() self.stackList = {} end function stackMng:pop() if #self.stackList == 0 then return end if self.stackList[1] then print(self.stackList[1]) end return table.remove(self.stackList,1) end function stackMng:push(t) table.insert(self.stackList,t) end function stackMng:Count() return #self.stackList end --測試代碼 object = stackMng:new() object:init() object:push(1) object:pop()
在Lua中用table實現集合是很是簡單的,見以下代碼:
reserved = { ["while"] = true, ["end"] = true, ["function"] = true, ["local"] = true, } for k,v in pairs(reserved) do print(k,"->",v) end