ua是一門腳本動態語言,并不太適合做復雜業務邏輯的程序開發,但是,在高并發場景下,Nginx Lua編程是解決性能問題的利器。
Nginx Lua編程主要的應用場景如下:
ngx_lua是Nginx的一個擴展模塊,將Lua VM嵌入Nginx,請求時創建一個VM,請求結束時回收VM,這樣就可以在Nginx內部運行Lua腳本,使得Nginx變成一個Web容器。以OpenResty為例,其提供了一些常用的ngx_lua開發模塊:
Lua腳本需要通過Lua解釋器來解釋執行,除了Lua官方的默認解釋器外,目前使用廣泛的Lua解釋器叫做LuaJIT。LuaJIT采用C語言編寫,被設計成全兼容標準Lua 5.1,因此LuaJIT代碼的語法和標準Lua的語法沒多大區別。但是LuaJIT的運行速度比標準Lua快數十倍。
在OpenResty中,每個Worker進程使用一個Lua VM,當請求被分配到Worker時,將在這個Lua VM中創建一個協程,協程之間數據隔離,每個協程都具有獨立的全局變量。
ngx_lua是將Lua VM嵌入Nginx,讓Nginx執行Lua腳本,并且高并發、非阻塞地處理各種請求。Lua內置協程可以很好地將異步回調轉換成順序調用的形式。ngx_lua在Lua中進行的IO操作都會委托給Nginx的事件模型,從而實現非阻塞調用。開發者可以采用串行的方式編寫程序,ngx_lua會在進行阻塞的IO操作時自動中斷,保存上下文,然后將IO操作委托給Nginx事件處理機制,在IO操作完成后,ngx_lua會恢復上下文,程序繼續執行,這些操作對用戶程序都是透明的。
每個Worker進程都持有一個Lua解釋器或LuaJIT實例,被這個Worker處理的所有請求共享這個實例。每個請求的context上下文會被Lua輕量級的協程分隔,從而保證每個請求是獨立的。
ngx_lua采用one-coroutine-per-request的處理模型,對于每個用戶請求,ngx_lua會喚醒一個協程用于執行用戶代碼處理請求,當請求處理完成后,這個協程會被銷毀。每個協程都有一個獨立的全局環境,繼承于全局共享的、只讀的公共數據。所以,被用戶代碼注入全局空間的任何變量都不會影響其他請求的處理,并且這些變量在請求處理完成后會被釋放,這樣就保證所有的用戶代碼都運行在一個sandbox(沙箱)中,這個沙箱與請求具有相同的生命周期。
得益于Lua協程的支持,ngx_lua在處理10000個并發請求時,只需要很少的內存。根據測試,ngx_lua處理每個請求只需要2KB的內存,如果使用LuaJIT就會更少。
ngx_lua定義的Nginx配置指令大致如下:
ngx_lua配置指令在Nginx的HTTP請求處理階段所處的位置如圖:
http {
...
#設置“.lua”外部庫的搜索路徑,此指令的上下文為http配置塊
#";;"常用于表示原始的搜索路徑
lua_package_path "/foo/bar/?.lua;/blah/?.lua;;";
lua_package_cpath "/usr/local/openresty/lualib/?/?.so;/usr/local/openresty/lualib/?.so;;";
}
對于以上兩個指令,OpenResty可以在搜索路徑中使用插值變量。例如,可以使用插值變量$prefix或${prefix}獲取虛擬服務器server的前綴路徑,server的前綴路徑通常在Nginx服務器啟動時通過-p PATH命令在指定。
格式為:init_by_lua lua-script-str。
格式為:lua_code_cache on | off
http {
...
#項目初始化
init_by_lua_file conf/luaScript/initial/loading_config.lua;
#調試模式,關閉lua腳本緩存
lua_code_cache on;
...
}
在緩存關閉的時,set_by_lua_file、content_by_lua_file、access_by_lua_file、content_by_lua_file等指令中引用的Lua腳本都將不會被緩存,所有的Lua腳本都將從頭開始加載。
格式為:set_by_lua $destVar lua-script-str params
location /set_by_lua_demo {
#set 指令定義兩個Nginx變量
set $foo 1;
set $bar 2;
#調用Lua內聯代碼,將結果放入Nginx變量$sum
#Lua腳本的含義是,將兩個輸入參數$foo、$bar累積起來,然后相加的結果設置Nginx變量$sum中
set_by_lua $sum 'return tonumber(ngx.arg[1]) + tonumber(ngx.arg[2])' $foo $bar;
echo "$foo + $bar=$sum";
}
運行結果:
? work curl http://localhost/set_by_lua_demo
1 + 2=3
格式為:access_by_lua $destVar lua-script-str
location /access_demo {
access_by_lua 'ngx.log(ngx.DEBUG, "remote_addr="..ngx.var.remote_addr);
if ngx.var.remote_addr=="192.168.56.121" then
return;
end
ngx.exit(ngx.HTTP_UNAUTHORIZED);
';
echo "hello world";
}
location /access_demo_2 {
allow "192.168.56.121";
deny all;
echo "hello world";
}
運行結果:
? work curl http://localhost/access_demo
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>openresty/1.13.6.2</center>
</body>
</html>
#上述案例運行日志:
2022/02/15 10:32:17 [debug] 26293#0: *17 [lua] access_by_lua(nginx-lua-demo.conf:85):1: remote_addr=127.0.0.1
2022/02/15 10:32:17 [info] 26293#0: *17 kevent() reported that client 127.0.0.1 closed keepalive connection
? work curl http://localhost/access_demo_2
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>openresty/1.13.6.2</center>
</body>
</html>
#上述案例運行日志
2022/02/15 10:33:11 [error] 26293#0: *18 access forbidden by rule, client: 127.0.0.1, server: localhost, request: "GET /access_demo_2 HTTP/1.1", host: "localhost"
2022/02/15 10:33:11 [info] 26293#0: *18 kevent() reported that client 127.0.0.1 closed keepalive connection
格式為:content_by_lua lua-script-str
location /errorLog {
content_by_lua '
ngx.log(ngx.ERR, "this is an error log ");
ngx.say("錯誤日志調用成功");
';
}
location /infoLog {
content_by_lua '
ngx.log(ngx.ERR, "this is an info log ");
ngx.say("業務日志調用成功");
';
}
location /debugLog {
content_by_lua '
ngx.log(ngx.ERR, "this is an debug log ");
ngx.say("調試日志調用成功");
';
}
OpenResty v0.9.17版本以后,使用content_by_lua_block指令代替content_by_lua指令,避免對代碼塊中的字符串進行轉譯。
運行結果:
? work curl http://localhost/errorLog
錯誤日志調用成功
? work curl http://localhost/infoLog
業務日志調用成功
? work curl http://localhost/debugLog
調試日志調用成功
內置變量
內置常量
內置常量基本是見名知意的,可以根據后面的實戰案例,加深理解。
核心常量
HTTP方法常量
HTTP狀態碼常量
日志類型常量
Nginx+LUA基礎到此結束,下一篇開始實戰!并在實戰中掌握基礎。
Lua基礎語法 -Lua教程?
Lua 教程_白玉搜一搜
---- 注釋
--這是 lua 的單行注釋
--[[
這是 lua 的多行注釋
第二行
第三行
--]]
--[[
這是 lua 的多行注釋
第二行
第三行
]]
> io.write("Hello world, from ",_VERSION,"!\n")
Hello world, from Lua 5.4!
file (0x7fff889b2ee8)
> v=1
> print(type(v));
number
> print(type(vv));
nil
>
-- 20分配給變量g,30分配給變量l
> g,l=20,30
-- type
> print(type("What is my type")) --> string
string
> t=10
> print(type(5.8*t)) --> number
number
> print(type(true)) --> boolean
boolean
> print(type(print)) --> function
function
> print(type(nil)) --> nil
nil
> print(type(type(ABC))) --> string
string
Lua標識符是用于標識變量,函數或任何其他用戶定義項的名稱。 標識符以字母A到Z或a到z或下劃線_開頭,后跟零個或多個字母,下劃線和數字(0到9)。
Lua不允許標識符中的標點符號,如@,$和%。 Lua是一種區分大小寫的編程語言。 因此,Yiibai和yiibai是Lua中的兩個不同的標識符。 以下是可接受標識符的一些示例
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
編號 | 值類型 | 描述 |
1 | nil | 用于區分值與某些數據或沒有(nil)數據。 |
2 | boolean | 包括true和false作為值,通常用于條件檢查。 |
3 | number | 表示實數(雙精度浮點)數字。 |
4 | string | 表示字符數組。 |
5 | function | 表示用C語言或Lua編寫的方法。 |
6 | userdata | 表示任意C語言數據。 |
7 | thread | 表示獨立的執行線程,它用于實現協同程序。 |
8 | table | 表示普通數組,符號表,集合,記錄,圖形,樹等,并實現關聯數組。 它可以保存任何值(除了nil)。 |
運算符 | 描述 | 示例 |
+ | 相加兩個操作數 | A + B=30 |
- | 從第一個減去第二個操作數 | A - B=-10 |
* | 將兩個操作數相乘 | A * B=200 |
/ | 用除分子除以分子 | B / A=2 |
% | 模數運算符,整數除法后的余數 | B % A=0 |
^ | 指數運算符取指數冪值 | A^2=100 |
- | 一元,充當否定 | -A=-10 |
運算符 | 描述 | 示例 |
== | 檢查兩個操作數的值是否相等,如果相等,則條件變為真。 | (A==B)結果為false |
~= | 檢查兩個操作數的值是否相等,如果值不相等則條件變為true。 | (A ~=B)結果為true |
> | 檢查左操作數的值是否大于右操作數的值,如果是,則條件變為true。 | (A > B)結果為false |
< | 檢查左操作數的值是否小于右操作數的值,如果是,則條件變為true。 | (A < B)結果為true |
>= | 檢查左操作數的值是否大于或等于右操作數的值,如果是,則條件變為true。 | (A >=B)結果為false |
<= | 檢查左操作數的值是否小于或等于右操作數的值,如果是,則條件變為true。 | (A <=B)結果為true |
運算符 | 描述 | 示例 |
and | 邏輯與運算符。如果兩個操作數都不為零,則條件成立。 | (A and B) 結果為false |
or | 邏輯或運算符。 如果兩個操作數中的任何一個不為零,則條件變為真。 | (A or B) 結果為true |
not | 邏輯非運算符。用于反轉其操作數的邏輯狀態。 如果條件為真,則邏輯非運算符將為false。 | !(A and B)結果為true |
編號 | 描述 | 示例 |
.. | 連接兩個字符串 | 如果a為Hello,b為World,a..b將返回Hello World。 |
# | 返回字符串或表長度的一元運算符。 | #"Hello" 將返回 5 |
具有最高優先級的運算符顯示在表的頂部,具有最低優先級的運算符顯示在底部。 在表達式中,將首先評估更高優先級的運算符。
類別 | 操作符 | 關聯性 |
一元 | not # - | 右到左 |
連接 | .. | 右到左 |
乘法 | * / % | 左到右 |
加法 | + - | 左到右 |
關系 | < > <=>===~= | 左到右 |
相等 | ==~= | 左到右 |
邏輯與 | and | 左到右 |
邏輯或 | or | 左到右 |
Lua提供以下類型的循環來處理循環需求。
編號 | 循環類型 | 描述 | |
1 | while循環 | 在給定條件為真時重復語句或語句組,它在執行循環體之前測試條件。 | while(condition) |
2 | for循環 | 多次執行一系列語句,并縮寫管理循環變量的代碼。 | for init,max/min value, increment |
3 | repeat…unti循環 | 重復語句組的操作,直到滿足until條件。 | repeat |
4 | 嵌套循環 | 可在任何循環中使用另一個或多個循環,如:while,for或do..while循環。 |
循環控制語句從正常順序改變執行。 當執行離開作用域時,將銷毀在該作用域中創建的所有自動對象。
Lua支持以下控制語句。
編號 | 控制語句 | 描述 |
1 | break語句 | 終止循環并將執行轉移到循環或switch之后的語句。 |
如果條件永遠不會變為假,則循環變為無限循環。 while循環通常用于此目的。如果直接給出了一個條件為真,它就會永遠執行。可以使用break語句來打斷這個無限循環。
while( true )
do
print("This loop will run forever.")
end
Lua編程語言假定布爾true和non-nil值的任意組合為true,如果它是布爾false或nil,則假定為false值。 需要注意的是,在Lua中,零將被視為true。
Lua編程語言提供以下類型的決策制定語句 -
編號 | 決策語句 | 描述 |
1 | if語句 | if語句由布爾表達式后跟一個或多個語句組成。 |
2 | if…else語句 | if語句后面可以跟一個可選的else語句,該語句在布爾表達式為false時執行。 |
3 | 嵌套if語句 | 在一個if或else if語句中使用另一個if或else if語句。 |
Lua編程語言中方法定義的一般形式如下 -
optional_function_scope function function_name( argument1, argument2, argument3........, argumentn)
function_body
return result_params_comma_separated
end
Lua編程語言中的方法定義由方法頭和方法體組成。以下是方法的所有部分 -
字符串是由一系列字符以及控制字符組成,如換頁符。 字符串可以用三種形式初始化,包括 -
string1="Lua"
print("\"String 1 is\"",string1)
string2='Yiibai Tutorial'
print("String 2 is",string2)
string3=[["Lua Tutorial"]]
print("String 3 is",string3)
字符串中使用轉義序列字符來更改字符的正常解釋。例如,要打印雙引號(""),在上面的示例中使用了"。轉義序列及其用法列在下表中。
轉義序列 | 用法 |
\a | 鈴 |
\b | 退格 |
\f | 換頁 |
\n | 新行 |
\r | 回車 |
\t | 制表符 |
\v | 垂直制表符 |
\ | 反斜杠 |
" | 雙引號 |
' | 單引號 |
\[ | 左方括號 |
\] | 右方括號 |
Lua支持字符串來操作字符串 -
編號 | 方法 | 作用 |
1 | string.upper(argument) | 返回參數的大寫表示。 |
2 | string.lower(argument) | 返回參數的小寫表示。 |
3 | string.gsub(mainString,findString,replaceString) | 返回用replaceString替換findString后字符串。 |
4 | string.find(mainString,findString, optionalStartIndex,optionalEndIndex) | 返回mainString中findString的起始索引和結束索引找到字符串,如果未找到則返回nil。 |
5 | string.reverse(arg) | 將傳遞的字符串arg反轉后的字符串。 |
6 | string.format(...) | 返回格式化字符串。 |
7 | string.char(arg) 和 string.byte(arg) | 返回輸入參數的內部數字和字符表示。 |
8 | string.len(arg) | 返回傳遞字符串arg的長度。 |
9 | string.rep(string, n)) | 通過重復相同的字符串n次返回一個字符串。 |
10 | .. | 此運算符連接兩個字符串。 |
數組是對象的有序排列,可以是包含行集合的一維數組或包含多個行和列的多維數組。
在Lua中,使用帶整數的索引表實現數組。 數組的大小不固定,它可以根據要求增長,受內存限制。
array={"Lua", "Tutorial"}
for i=0, 2 do
print(array[i])
end
-- 初始化數組
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
迭代器泛型提供集合中每個元素的鍵值對。 下面給出一個簡單的例子。
array={"Lua", "Tutorial"}
for key,value in ipairs(array)
do
print(key, value)
end
上面的例子使用了Lua提供的默認ipairs迭代器函數。
在Lua中,使用函數來表示迭代器。 基于這些迭代器函數中的狀態維護,有兩種主要類型 -
通過名稱,可以理解這種類型的迭代器函數不會保留任何狀態。下面來看一個使用打印n個數字的函數square創建迭代器的示例。
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
稍微修改上面的代碼,以模仿迭代器的ipairs函數的工作方式。 如下代碼所示 -
function square(iteratorMaxCount,currentNumber)
if currentNumber<iteratorMaxCount
then
currentNumber=currentNumber+1
return currentNumber, currentNumber*currentNumber
end
end
function squares(iteratorMaxCount)
return square,iteratorMaxCount,0
end
for i,n in squares(3)
do
print(i,n)
end
上面使用函數的迭代示例不保留狀態。 每次調用該函數時,它都會根據發送給函數的第二個變量返回集合的下一個元素。 要保持當前元素的狀態,可使用閉包(Closure)。 閉包在函數調用中保留變量值。 要創建一個新的閉包,這里首先創建兩個函數,包括閉包本身和一個工廠,即創建閉包的函數。
現在來看看一個創建迭代器的例子,并將使用閉包。
array={"Lua", "Tutorial"}
function elementIterator (collection)
local index=0
local count=#collection
-- The closure function is returned
return function ()
index=index + 1
if index <=count
then
-- return the current element of the iterator
return collection[index]
end
end
end
for element in elementIterator(array)
do
print(element)
end
當運行上面的程序時,將得到以下輸出 -
Lua
Tutorial
在上面的例子中,可以看到elementIterator中有另一個方法,它使用局部外部變量index和count來通過每次調用函數時遞增索引來返回集合中的每個元素。 可使用閉包創建函數迭代器,如上所示,它可以為迭代整個集合的每個時間返回多個元素。
表是Lua中唯一可用的數據結構,使用表可以創建不同的類型,如數組和字典。 Lua使用關聯數組,不僅可使用數字編制索引,還可以使用除nil之外的字符串編制索引。 表沒有固定的大小,可以根據需要增長大小。
Lua在所有表示中使用表,包括包的表示。 當訪問方法string.format時,這意味著,訪問字符串包中可用的格式函數。
表稱為對象,它既不是值也不是變量。 Lua使用構造函數表達式{}來創建一個空表。 應該知道,保持表的引用的變量和表本身之間沒有固定的關系。
--sample table initialization
mytable={}
--simple table value assignment
mytable[1]="Lua"
--removing reference
mytable=nil
-- lua garbage collection will take care of releasing memory
假設有一個包含元素集的表a,如果將它分配給表b,則a和b都指向相同的內存。 Lua不會單獨為b分配單獨的內存。 當a設置為nil時,b仍然可以訪問表。 當沒有對表的引用時,Lua中的垃圾收集器負責清理進程以使這些未引用的內存再次被重用。
下面示出了一個例子,用于解釋表的上述特征。
-- Simple empty table
mytable={}
print("Type of mytable is ",type(mytable))
mytable[1]="Lua"
mytable["wow"]="Tutorial"
print("mytable Element at index 1 is ", mytable[1])
print("mytable Element at index wow is ", mytable["wow"])
-- alternatetable and mytable refers to same table
alternatetable=mytable
print("alternatetable Element at index 1 is ", alternatetable[1])
print("mytable Element at index wow is ", alternatetable["wow"])
alternatetable["wow"]="I changed it"
print("mytable Element at index wow is ", mytable["wow"])
-- only variable released and and not table
alternatetable=nil
print("alternatetable is ", alternatetable)
-- mytable is still accessible
print("mytable Element at index wow is ", mytable["wow"])
mytable=nil
print("mytable is ", mytable)
當運行上述程序時,將獲得以下輸出 -
Type of mytable is table
mytable Element at index 1 is Lua
mytable Element at index wow is Tutorial
alternatetable Element at index 1 is Lua
mytable Element at index wow is Tutorial
mytable Element at index wow is I changed it
alternatetable is nil
mytable Element at index wow is I changed it
mytable is nil
下面是用于表操作的內置函數,它們列在下表格中。
編號 | 方法 | 作用 |
1 | table.concat(table [, sep [, i [, j]]]) | 根據給定的參數連接表中的字符串。詳細信息請參見示例。 |
2 | table.insert(table, [pos,] value) | 在指定位置的表中插入值。 |
3 | table.maxn(table) | 返回最大的數字索引。 |
4 | table.remove(table [, pos]) | 從表中刪除值。 |
5 | table.sort(table [, comp]) | 根據可選的比較器參數對表進行排序。 |
下面來看看一些上述功能的示例。
使用concat函數來連接兩個表,如下所示 -
fruits={"banana","orange","apple"}
-- returns concatenated string of table
print("Concatenated string ",table.concat(fruits))
--concatenate with a character
print("Concatenated string ",table.concat(fruits,", "))
--concatenate fruits based on index
print("Concatenated string ",table.concat(fruits,", ", 2,3))
當運行上述程序時,將獲得以下輸出 -
Concatenated string bananaorangeapple
Concatenated string banana, orange, apple
Concatenated string orange, apple
在表操作中,最常見的是在表中插入和刪除項目。如下解釋。
fruits={"banana","orange","apple"}
-- insert a fruit at the end
table.insert(fruits,"mango")
print("Fruit at index 4 is ",fruits[4])
--insert fruit at index 2
table.insert(fruits,2,"grapes")
print("Fruit at index 2 is ",fruits[2])
print("Fruit at index 2 is ",table.concat(fruits,", ") )
-- banana, orange, apple, mango --> banana, grapes, orange, apple, mango
print("The maximum elements in table is",#fruits)
--print("The maximum elements in table is",table.maxn(fruits))
print("The last element is",fruits[5])
table.remove(fruits)
print("The previous last element is",fruits[5])
當運行上述程序時,將獲得以下輸出 -
Fruit at index 4 is mango
Fruit at index 2 is grapes
The maximum elements in table is 5
The last element is mango
The previous last element is nil
有時想要按特定順序對表進行排序。 排序函數按字母順序對表中的元素進行排序。 這方面的示例如下所示。
fruits={"banana","orange","apple","grapes"}
for k,v in ipairs(fruits) do
print(k,v)
end
table.sort(fruits)
print("sorted table")
for k,v in ipairs(fruits) do
print(k,v)
end
當運行上述程序時,將獲得以下輸出 -
1 banana
2 orange
3 apple
4 grapes
sorted table
1 apple
2 banana
3 grapes
4 orange
模塊是一個可以使用require加載的庫,并且只有一個包含表的全局名稱。 模塊可以包含許多功能和變量。 所有這些函數和變量都包含在表中,表充當命名空間。 此外,一個良好的模塊有必要的子句,以在使用require語句時返回此表。
模塊中表的使用以多種方式,能夠使用與操作任何其他Lua表相同的方式操作模塊。 由于能夠操作模塊,它提供了其他語言需要特殊機制的額外功能。 由于Lua中模塊的這種自由機制,用戶可以通過多種方式調用Lua中的函數。 其中一些操作示例如下所示。
-- Assuming we have a module printFormatter
-- Also printFormatter has a funtion simpleFormat(arg)
-- Method 1
require "printFormatter"
printFormatter.simpleFormat("test")
-- Method 2
local formatter=require "printFormatter"
formatter.simpleFormat("test")
-- Method 3
require "printFormatter"
local formatterFunction=printFormatter.simpleFormat
formatterFunction("test")
在上面的示例代碼中,可以看到Lua中的編程靈活性,沒有任何特殊的附加代碼。
Lua提供了一個名為require的高級函數來加載所有必需的模塊。 它保持盡可能簡單,以避免有太多關于模塊的信息來加載。 require函數只是將模塊假定為一塊代碼,它定義了一些值,實際上是包含函數或表。
示例
考慮一個簡單的例子,其中一個函數是數學函數。 將此模塊稱為mymath,文件名為mymath.lua。 文件的代碼內容如下 -
local mymath={}
function mymath.add(a,b)
print(a+b)
end
function mymath.sub(a,b)
print(a-b)
end
function mymath.mul(a,b)
print(a*b)
end
function mymath.div(a,b)
print(a/b)
end
return mymath
現在,為了在另一個文件(例如,moduletutorial.lua)中訪問此Lua模塊,需要使用以下代碼段。
package.path='/Users/fei/work/lua_work/learn/mymath.lua'
mymathmodule=require("mymath")
mymathmodule.add(10,20)
mymathmodule.sub(30,20)
mymathmodule.mul(10,20)
mymathmodule.div(30,20)
要運行此代碼,需要將兩個Lua文件放在同一目錄中,或者,可以將模塊文件放在包路徑(package.path)中,它需要額外的設置。 當運行上面的程序時,將得到以下輸出 -
30
10
200
1.5
注意事項
下面將以舊方式重寫上面相同的示例,它使用package.seeall類型的實現。 這在Lua版本5.1和5.0中使用。 mymath模塊如下所示。
module("mymath", package.seeall)
function mymath.add(a,b)
print(a+b)
end
function mymath.sub(a,b)
print(a-b)
end
function mymath.mul(a,b)
print(a*b)
end
function mymath.div(a,b)
print(a/b)
end
moduletutorial.lua 中模塊的用法如下所示:
require("mymath")
mymath.add(10,20)
mymath.sub(30,20)
mymath.mul(10,20)
mymath.div(30,20)
當運行上面的操作時,將獲得相同的輸出。 但建議使用較舊版本的代碼,并假設它不太安全。 許多使用Lua進行編程的SDK如Corona SDK都不推薦使用它。
元表(metatable)是一個表,它是使用鍵集和相關元方法來修改附加到的表的行為。 這些元方法是強大的Lua功能,可實現如下功能 -
在處理元表時有兩種重要的方法,包括 -
首先來看看如何將一個表設置為另一個表的元表。 如下所示 -
mytable={}
mymetatable={}
setmetatable(mytable,mymetatable)
getmetatable(mytable)
上面的代碼可以用一行表示,如下所示 -
mytable=setmetatable({},{})
getmetatable(mytable)
__index -- 'table[key]',取下標操作,用于訪問表中的域
__newindex -- 'table[key]=value',賦值操作,增改表中的域
__call -- 'func(args)',函數調用,(參見 《Lua 學習筆記(三)—— 表達式》中的函數部分介紹)
-- 數學運算操作符
__add -- '+'
__sub -- '-'
__mul -- '*'
__div -- '/'
__mod -- '%'
__pow -- '^'
__unm -- '-'
-- 連接操作符
__concat -- '..'
-- 取長操作符
__len -- '#'
-- 比較操作符
__eq -- '=='
__lt -- '<' -- a > b 等價于 b < a
__le -- '<=' -- a >=b 等價于 b <=a
## 參考: https://blog.csdn.net/yangzhenzhen/article/details/51233548
下表顯示了元表在表中不可用時查找元表的示例。
mytable=setmetatable({key1="value1"}, {
__index=function(mytable, key)
if key=="key2" then
return "metatablevalue"
else
return mytable[key]
end
end
})
print(mytable.key1,mytable.key2)
當運行上面的程序時,將得到以下輸出結果 -
value1 metatablevalue
下面來逐步看看上面例子中發生的事情。
上述程序的簡化版本,如下所示 -
mytable=setmetatable({key1="value1"},{ __index={ key2="metatablevalue" } })
print(mytable.key1,mytable.key2)
當將__newindex添加到metatable時,如果表中沒有鍵,則新鍵的行為將由元方法定義。 下面給出了當主表中沒有索引時設置metatable索引的簡單示例。
mymetatable={}
mytable=setmetatable({key1="value1"}, { __newindex=mymetatable })
print(mytable.key1)
mytable.newkey="new value 2"
print(mytable.newkey,mymetatable.newkey)
mytable.key1="new value 1"
print(mytable.key1,mymetatable.newkey1)
運行上述程序時,將獲得以下輸出 -
value1
nil new value 2
new value 1 nil
在上面的程序中看到,如果主表中存在一個鍵,它只會更新它。 當維護中的鍵不可用時,它會將該鍵添加到metatable中。
使用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"
rawset設置值而不使用元表的__newindex。 類似地,有一個rawget可以在不使用__index的情況下獲取值。
使用+運算符組合兩個表的簡單示例如下所示 -
mytable=setmetatable({ 1, 2, 3 }, {
__add=function(mytable, newtable)
for i=1, (#newtable) do
table.insert(mytable, (#mytable)+1,newtable[i])
end
return mytable
end
})
secondtable={4,5,6}
mytable=mytable + secondtable
print(table.concat(mytable," , "))
for k,v in ipairs(mytable) do
print(k,v)
end
當運行上面的程序時,將得到以下輸出 -
1 , 2 , 3 , 4 , 5 , 6
1 1
2 2
3 3
4 4
5 5
6 6
__add鍵包含在元表中以添加運算符 + 的行為。鍵表和相應的操作符如下所示。
編號 | 模式 | 描述 |
1 | __add | 改變運算符+的行為。 |
2 | __sub | 改變運算符-的行為。 |
3 | __mul | 改變運算符*的行為。 |
4 | __div | 改變運算符/的行為。 |
5 | __mod | 改變運算符%的行為。 |
6 | __unm | 改變運算符-的行為。 |
7 | __concat | 改變運算符..的行為。 |
8 | __eq | 改變運算符==的行為。 |
9 | __lt | 改變運算符<的行為。 |
10 | __le | 改變運算符<=的行為。 |
使用__call語句添加方法調用的行為。 一個簡單的示例,它返回主表中的值與傳遞的表的總和。
mytable=setmetatable({10}, {
__call=function(mytable, newtable)
sum=0
for i=1, (#mytable) do
sum=sum + mytable[i]
end
for i=1, (#newtable) do
sum=sum + newtable[i]
end
return sum
end
})
newtable={10,20,30}
print(mytable(newtable))
當運行上面的程序時,將得到以下輸出。
70
要更改print語句的行為,可以使用__tostring元方法。 一個簡單的例子如下所示。
mytable=setmetatable({ 10, 20, 30 }, {
__tostring=function(mytable)
sum=0
for k, v in pairs(mytable) do
sum=sum + v
end
return "The sum of values in the table is " .. sum
end
})
print(mytable)
當運行上面的程序時,將得到以下輸出。
The sum of values in the table is 60
Shell
如果完全了解元表的功能,那么可以真正執行很多非常復雜的操作。 因此,嘗試使用元表中可用的不同選項的元表更多地工作。
協同程序本質上是協作的,它允許兩種或多種方法以受控方式執行。 使用協同程序,在任何給定時間,只有一個協同程序運行,并且此運行協程僅在顯式請求暫停時暫停執行。
上述定義可能看起來含糊不清。 假設有兩種方法,
一種是主程序方法,另一種是協程。 當使用resume函數調用一個協程時,它會開始執行,當調用yield函數時,它會暫停執行。
同樣的協同程序可以繼續執行另一個恢復函數調用,協同程序就會暫停。 該過程可以持續到協程執行結束。
下表列出了Lua中協程的所有可用函數及其相應的用法。
編號 | 方法 | 作用或目的 |
1 | coroutine.create (f) | 使用函數f創建一個新的協同程序,并返回thread類型的對象。 |
2 | coroutine.resume (co [, val1, ...]) | 恢復協程co并傳遞參數(如果有的話)。它返回操作狀態和可選的其他返回值。 |
3 | coroutine.running () | 如果在主線程中調用,則返回正在運行的協同程序或nil。 |
4 | coroutine.status (co) | 根據協同程序的狀態返回running,normal,suspended或dead中的一個值。 |
5 | coroutine.wrap (f) | 與coroutine.create一樣,coroutine.wrap函數也會創建一個協同程序,但它不會返回協同程序本身,而是返回一個函數,當調用它時,它會恢復協同程序。 |
6 | coroutine.yield (...) | 暫停正在運行的協同程序。 傳遞給此方法的參數充當resume函數的附加返回值。 |
示例 下面來看一個例子,通過此示例來理解協同程序的概念。
co=coroutine.create(function (value1,value2)
local tempvar3=10
print("coroutine section 1", value1, value2, tempvar3)
local tempvar1=coroutine.yield(value1+1,value2+1)
tempvar3=tempvar3 + value1
print("coroutine section 2",tempvar1 ,tempvar2, tempvar3)
local tempvar1, tempvar2=coroutine.yield(value1+value2, value1-value2)
tempvar3=tempvar3 + value1
print("coroutine section 3",tempvar1,tempvar2, tempvar3)
return value2, "end"
end)
print("main", coroutine.resume(co, 3, 2))
print("main", coroutine.resume(co, 12,14))
print("main", coroutine.resume(co, 5, 6))
print("main", coroutine.resume(co, 10, 20))
當運行上面的程序時,將得到以下輸出 -
coroutine section 1 3 2 10
main true 4 3
coroutine section 2 12 nil 13
main true 5 1
coroutine section 3 5 6 16
main true 2 end
main false cannot resume dead coroutine
Shell
上面的例子是實現什么功能?
如前所述,使用resume函數來啟動操作和yield函數來停止操作。 此外,可以看到coroutine的恢復功能接收到多個返回值。
下面來看一個簡單的協同程序示例,它使用yield函數和resume函數返回1到5之間的數字。 如果不可用它會創建協程,或者恢復現有的協程。
function getNumber()
local function getNumberHelper()
co=coroutine.create(function ()
coroutine.yield(1)
coroutine.yield(2)
coroutine.yield(3)
coroutine.yield(4)
coroutine.yield(5)
end)
return co
end
if(numberHelper) then
status, number=coroutine.resume(numberHelper);
if coroutine.status(numberHelper)=="dead" then
numberHelper=getNumberHelper()
status, number=coroutine.resume(numberHelper);
end
return number
else
numberHelper=getNumberHelper()
status, number=coroutine.resume(numberHelper);
return number
end
end
for index=1, 10 do
print(index, getNumber())
end
當運行上面的程序時,將得到以下輸出。
1 1
2 2
3 3
4 4
5 5
6 1
7 2
8 3
9 4
10 5
通常會將協同程序與多路程序設計語言的線程進行比較,但需要了解協同程序具有類似的線程功能,但協同程序一次只執行一個程序,并且永遠不會同時執行。
通過暫時保留某些信息來控制程序執行順序以滿足需求。 使用帶有協同程序的全局變量為協同程序提供了更大的靈活性。
I/O庫用于讀取和操作Lua中的文件。 Lua中有兩種文件操作,即隱式文件描述符和顯式文件描述符。
對于以下示例,將使用示例文件 - test.lua,內容如下所示 -
-- sample test.lua
-- sample2 test.lua
文件打開操作使用以下語句 -
file=io.open (filename [, mode])
下表列出了各種文件模式 -
編號 | 模式 | 描述 |
1 | r | 只讀模式,是打開現有文件的默認模式。 |
2 | w | 寫入啟用模式,覆蓋現有文件或創建新文件。 |
3 | a | 附加模式,用于打開現有文件或創建新文件以進行追加。 |
4 | r+ | 現有文件的讀寫模式。 |
5 | w+ | 如果文件存在或創建具有讀寫權限的新文件,則刪除所有現有數據。 |
6 | a+ | 啟用了讀取模式的追加模式可打開現有文件或創建新文件。 |
隱式文件描述符使用標準輸入/輸出模式或使用單個輸入和單個輸出文件。 使用隱式文件描述符的示例如下所示。
-- Opens a file in read
file=io.open("test.lua", "r")
-- sets the default input file as test.lua
io.input(file)
-- prints the first line of the file
print(io.read())
-- closes the open file
io.close(file)
-- Opens a file in append mode
file=io.open("test.lua", "a")
-- sets the default output file as test.lua
io.output(file)
-- appends a word test to the last line of the file
io.write("-- End of the test.lua file\n")
-- closes the open file
io.close(file)
運行程序時,將獲得test.lua 文件第一行的輸出。執行上面程序,得到以下輸出 -
-- Sample test.lua
這是test.lua 文件中聲明的第一行。 同時也將"-- End of the test.lua file"這一行附加到test.lua 文件中的最后一行。
在上面的示例中,可以使用io."x"方法查看隱式描述符如何與文件系統一起使用。 上面的示例使用不帶可選參數的io.read()方法??蛇x參數可以是以面中的任何一個。
編號 | 模式 | 描述 |
1 | *n | 從當前文件位置讀取并返回一個數字(如果存在于文件位置或返回nil)。 |
2 | *a | 從當前文件位置返回文件的所有內容。 |
3 | *l | 從當前文件位置讀取行,并將文件位置移動到下一行。 |
4 | number | 讀取函數中指定的字節數。 |
其他常見的I/O方法包括,
顯式文件描述符經常使用,它允許一次操作多個文件。 這些函數與隱式文件描述符非常相似。 在這里,使用file:function_name,而不是io.function_name。 下面顯示了以下相同隱式文件描述符示例的顯式文件描述符版本示例。
-- Opens a file in read mode
file=io.open("test.lua", "r")
-- prints the first line of the file
print(file:read())
-- closes the opened file
file:close()
-- Opens a file in append mode
file=io.open("test.lua", "a")
-- appends a word test to the last line of the file
file:write("--test")
-- closes the open file
file:close()
運行程序時,將獲得與隱式描述符示例類似的輸出 -
-- Sample test.lua
文件打開的所有模式和用于讀取外部描述符的參數與隱式文件描述符相同。
其他常見的文件方法包括,
使用搜索方法的示例如下所示。它將光標從文件結束前的25個位置偏移。 read函數從搜索位置打印文件的剩余部分。
-- Opens a file in read
file=io.open("test.lua", "r")
file:seek("end",-25)
print(file:read("*a"))
-- closes the opened file
file:close()
執行上面示例代碼,將獲得類似于以下內容的輸出 -
sample2 test.lua
--test
讀者可自己使用所有的模式和參數來了解Lua文件操作的完整功能。
錯誤處理非常關鍵,因為實際操作通常需要使用復雜的操作,包括文件操作,數據庫事務和Web服務調用。
在任何編程中,總是需要錯誤處理。 錯誤可以是兩種類型,它們包括 -
由于不正確地使用各種程序組件(如運算符和表達式),從而發生語法錯誤。 語法錯誤的一個簡單示例如下所示-
a==2
使用單個“單等于號”和“雙等于號”之間存在差異。 使用不當可能導致錯誤。 一個“等于”指的是賦值,而兩個“等于”指的是比較。
語法錯誤的另一個示例如下所示 -
for a=1,10
print(a)
end
當運行上述程序時,將獲得以下輸出 -
lua: test2.lua:2: 'do' expected near 'print'
語法錯誤比運行時錯誤更容易處理,因為Lua解釋器比運行時錯誤更清楚地定位錯誤。 從上面的錯誤中,可以很容易地知道根據Lua結構在print語句之前添加do語句。
如果出現運行時錯誤,程序將成功執行,但由于輸入錯誤或錯誤處理的函數,可能會導致運行時錯誤。 顯示運行時錯誤的簡單示例如下所示。
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參數是預期的,但是因為它未傳入,默認使用的是nil從而產生錯誤。
要處理錯誤,經常使用兩個函數是 - assert和error。 一個簡單的例子如下所示。
local function add(a,b)
assert(type(a)=="number", "a is not a number")
assert(type(b)=="number", "b is not a number")
return a+b
end
add(10)
當構建上面程序時,它將成功構建并運行。 運行后,顯示運行時錯誤。
lua: test2.lua:3: b is not a number
stack traceback:
[C]: in function 'assert'
test2.lua:3: in function 'add'
test2.lua:6: in main chunk
[C]: ?
error(message [,level])終止最后一個調用的受保護函數,并將消息作為錯誤消息返回。 此函數錯誤永遠不會返回。 通常,錯誤會在消息開頭添加有關錯誤位置的一些信息。 level參數指定如何獲取錯誤位置。 對于級別1(默認值),錯誤位置是調用錯誤函數的位置。 級別2將錯誤指向調用調用錯誤的函數的位置等等。 傳遞0級可避免向消息添加錯誤位置信息。
在Lua編程中,為了避免拋出這些錯誤和處理錯誤,需要使用pcall或xpcall函數。
pcall(f,arg1,...)函數在保護模式下調用所請求的函數。 如果函數f中發生某些錯誤,則不會拋出錯誤。 它只返回錯誤狀態。 使用pcall的一個簡單示例如下所示。
function myfunction()
n=n/nil
end
if pcall(myfunction) then
print("Success")
else
print("Failure")
end
當構建上面程序時,它將成功構建并運行。 運行后,顯示運行時錯誤。
Failure
xpcall(f,err)函數調用所請求的函數,并設置錯誤處理程序。 f內的任何錯誤都不會傳播; 而xpcall只捕獲錯誤,使用原始錯誤對象調用err函數,并返回狀態代碼。
xpcall函數的一個簡單示例如下所示 -
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
作為程序員,最重要的是確保在編寫的程序中處理正確的錯誤處理。 使用錯誤處理可以確保處理超出邊界條件的意外條件而不會干擾程序的用戶。
Lua提供了一個調試庫,它提供了創建自己的調試器的原始函數。 即使沒有內置的Lua調試器,也有許多第三方Lua調試器,由各種開發人員創建,其中許多是開源的。
下表中列出了Lua調試庫中可用的功能及其用法。
編號 | 方法 | 描述 |
1 | debug() | 進入用于調試的交互模式,該模式保持活動狀態,直到用戶只輸入一行中的cont并按Enter鍵。 用戶可以使用其他功能在此模式下檢查變量。 |
2 | getfenv(object) | 返回對象的環境。 |
3 | gethook(optional thread) | 返回線程的當前掛鉤設置,有三個值 - 當前掛鉤函數,當前掛鉤掩碼和當前掛鉤計數。 |
4 | getinfo(optional thread, function or stack level, optional flag) | 返回一個包含函數信息的表??梢灾苯咏o出函數,或者可以給一個數字作為函數的值,在給定線程的調用堆棧的級別函數上運行的函數 - 級別0為當前函數(getinfo本身); 級別1為調用getinfo的函數;等等。 如果function是一個大于活動函數數的數字,則getinfo返回nil。 |
5 | getlocal(optional thread, stack level, local index) | |
6 | getmetatable(value) | 返回給定對象的元表,如果沒有元表,則返回nil。 |
7 | getregistry() | 返回注冊表表,這是一個預定義的表,任何C語言代碼都可以使用它來存儲它需要存儲的任何Lua值。 |
8 | getupvalue(function, upvalue index) | 此函數返回upvalue的名稱和值,索引為函數func。 如果給定索引沒有upvalue,則函數返回nil。 |
9 | setfenv(function or thread or userdata, environment table) | 將給定對象的環境設置為給定表,返回對象。 |
10 | sethook(optional thread, hook function, hook mask string with "c" and/or "r" and/or "l", optional instruction count) | 將給定函數設置為鉤子。 字符串掩碼和數字計數描述了何時調用掛鉤。 這里,每次Lua調用,返回并分別輸入函數中的每一行代碼時,都會調用c,r和l。 |
11 | setlocal(optional thread, stack level, local index, value) | 使用堆棧級別的函數的索引local將值賦給局部變量。 如果沒有具有給定索引的局部變量,則該函數返回nil,并且當使用超出范圍的級別調用時引發錯誤。 否則,它返回局部變量的名稱。 |
12 | setmetatable(value, metatable) | 將給定對象的metatable設置為給定表(可以為nil)。 |
13 | setupvalue(function, upvalue index, value) | 此函數使用函數func的索引up將值賦給upvalue。 如果給定索引沒有upvalue,則函數返回nil。 否則,它返回upvalue的名稱。 |
14 | traceback(optional thread, optional message string, optional level argument) | 使用回溯構建擴展錯誤消息。 |
上面的列表是Lua中完整的調試函數列表,經常使用一個上述函數的庫,并提供更簡單的調試。 使用這些函數并創建我們自己的調試器非常復雜,并不是優選的。 下面來看一個簡單使用調試功能的例子。
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.trace函數打印堆棧跟蹤。 debug.getinfo獲取函數的當前表。
開發人員經常為了知道函數的局部變量以進行調試。 為此,可以使用getupvalue和使用setupvalue并設置這些局部變量。 一個簡單的例子如下所示 -
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
在此示例中,計數器每次調用時都會更新一次。 可以使用getupvalue函數查看局部變量的當前狀態。 然后,將局部變量設置為新值。 這里,在調用set操作之前n為2。 使用setupvalue函數,它更新為10?,F在當調用計數器函數時,它將返回11而不是3。
在Lua中,調試類型主要有兩種 -
1. 命令行調試
命令行調試是使用命令和打印語句在命令行進行調試的調試類型。 Lua有許多命令行調試器,下面列出了一些。
2. 圖形調試
在IDE下可以進行圖形調試,在IDE中可為各種狀態(如變量值,堆棧跟蹤和其他相關信息)進行可視化調試。 借助斷點,步入,步進和IDE中的其他按鈕,可以實現可視化表示和逐步控制執行。
Lua有許多圖形調試器,它包括以下內容-
Lua使用自動內存管理,該管理使用基于Lua內置的某些算法的垃圾收集。自動內存管理收集,作為開發人員 -
Lua使用垃圾收集器它在后臺會時不時地運行,收集和銷毀Lua程序訪問的死對象。
包括表,用戶數據,函數,線程,字符串等在內的所有對象都需要進行自動內存管理。 Lua使用增量標記和掃描收集器,它使用兩個數字來控制其垃圾收集周期,即垃圾收集器暫停和垃圾收集器步驟倍增器。 這些值以百分比表示,值100通常在內部等于1。
垃圾收集器暫停用于控制垃圾收集器之前需要等待多長時間; Lua的自動內存管理再次調用。 小于100的值意味著Lua不會等待下一個周期。 類似地,該值的較高值將導致垃圾收集器緩慢。 200的值表示收集器在開始新循環之前等待使用的總內存加倍。 因此,根據應用程序的性質和速度,可能需要更改此值以在Lua應用程序中獲得最佳性能。
此步驟乘數控制垃圾收集器與Lua程序中內存分配的相對速度。 較大的步長值將導致垃圾收集器更具侵略性,并且還會增加垃圾收集的每個增量步驟的步長。 小于100的值通常可以避免垃圾收集器不完成循環并且通常不是優選的。 默認值為200,這意味著垃圾收集器的運行速度是內存分配的兩倍。
作為開發人員,確實可以控制Lua中的自動內存管理。有以下方法可以使用。
使用垃圾收集器示例如下所示。
mytable={"apple", "orange", "banana"}
print(collectgarbage("count"))
mytable=nil
print(collectgarbage("count"))
print(collectgarbage("collect"))
print(collectgarbage("count"))
當運行上面的程序時,將得到以下輸出。 請注意,由于操作系統類型的不同以及Lua的自動內存管理功能,此結果會有所不同。
22.599609375
20.79296875
0
21.5537109375
在上面的程序中看到,完成垃圾收集,它就減少了使用的內存。 但是,這并不是強制要求。 即使不調用它們,它也會在預定義的時間段后由Lua解釋器在稍后階段自動執行。
顯然,如果需要,可以使用這些函數來改變垃圾收集器的行為。 這些函數為開發人員提供了一些額外的功能來處理復雜的情況。 根據程序所需的內存類型,也可能不會使用此功能。 但了解應用程序中的內存使用情況并在編程期間檢查它以避免在部署后出現意外結果非常有用。
面向對象編程(OOP)是現代編程中使用最多的編程技術之一。 有許多支持OOP的編程語言包括 -
可使用Lua的表和第一類函數在Lua中實現面向對象。 通過將函數和相關數據放入表中,形成對象??墒褂迷韺崿F繼承,為父對象中不存在的函數(方法)和字段提供查找機制。
Lua中的表具有狀態和標識等對象的特征,與其值無關。 具有相同值的兩個對象(表)是不同的對象,而對象在不同的時間可以具有不同的值,但它始終是相同的對象。 與對象一樣,表的生命周期與誰創建它們或創建位置無關。
面向對象的概念廣泛使用,但需要清楚地理解面向對象以獲得適當和最大的好處。 考慮一個簡單的數學例子。 我們經常遇到處理不同形狀的情況,如圓形,矩形和方形。
形狀可以具有共同的屬性area。 因此,使用公共屬性區域從基礎對象形狀擴展其他形狀。 每個形狀都可以有自己的屬性,像矩形這樣的函數可以具有屬性length,width,area作為屬性,printArea和calculateArea作為它的函數。
下面顯示了具有三個屬性length,width和area的矩形的簡單類實現。 它還有一個printArea函數來打印計算區域面積。
-- Meta class
Rectangle={area=0, length=0, breadth=0}
-- Derived class method 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
-- Derived class method printArea
function Rectangle:printArea ()
print("The area of Rectangle is ",self.area)
end
創建一個對象 創建對象是為類實例分配內存的過程。 每個對象都有自己的內存并共享公共類數據。
r=Rectangle:new(nil,10,20)
訪問屬性 可以使用點(.)運算符訪問類中的屬性,如下所示 -
print(r.length)
訪問成員函數
使用帶有對象的冒號(:)運算符訪問成員函數,如下所示 -
r=Rectangle:new(nil,10,20)
r:printArea()
分配內存并設置初始值??梢詫⒊跏蓟^程與其他面向對象語言中的構造函數進行比較。 它只是一個能夠設置如上所示的值的函數。
完整的例子 下面來看一下在Lua中使用面向對象的完整示例。代碼如下 -
-- Meta class
Shape={area=0}
-- Base class method 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
-- Base class method printArea
function Shape:printArea ()
print("The area is ",self.area)
end
-- Creating an object
myshape=Shape:new(nil,10)
myshape:printArea()
運行上述程序時,將獲得以下輸出 -
The area is 100
繼承是將簡單的基礎對象(如形狀)擴展為矩形,正方形等的過程。 它經常在現實世界中用于共享和擴展基本屬性和功能。
下面來看一個簡單的類擴展。有一個如下所示的類,
-- 元類
Shape={area=0}
-- 基類方法
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("The area is ",self.area)
end
可以將形狀擴展為方形類,如下所示。
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
覆蓋基類函數
可以覆蓋基類函數而不是在基類中使用函數,派生類可以有自己的實現,如下所示 -
-- Derived class method printArea
function Square:printArea ()
print("The area of square is ",self.area)
end
繼承完整示例 在另一個new方法的幫助下,使用metatables,擴展Lua中的簡單類實現,如上所示。 基類的所有成員變量和函數都保留在派生類中。
-- Meta class
Shape={area=0}
-- Base class method 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
-- Base class method printArea
function Shape:printArea ()
print("The area is ",self.area)
end
-- Creating an object
myshape=Shape:new(nil,10)
myshape:printArea()
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
-- Derived class method printArea
function Square:printArea ()
print("The area of square is ",self.area)
end
-- Creating an object
mysquare=Square:new(nil,10)
mysquare:printArea()
Rectangle=Shape:new()
-- Derived class method 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
-- Derived class method printArea
function Rectangle:printArea ()
print("The area of Rectangle is ",self.area)
end
-- Creating an object
myrectangle=Rectangle:new(nil,10,20)
myrectangle:printArea()
當運行上述程序時,將獲得以下輸出 -
The area is 100
The area of square is 100
The area of Rectangle is 200
在上面的例子中,創建了兩個派生類 - 基類Square和Rectangle。并在派生類中覆蓋基類的函數。 在此示例中,派生類會覆蓋函數printArea。
Lua是一種高度靈活的語言,通常用于多種平臺,包括Web應用程序。 開普勒(Kepler)社區成立于2004年,旨在為Lua提供開源Web組件。
盡管已經有很多Lua Web開發是使用Lua的Web框架,但這里主要介紹和使用Kepler社區提供的組件。
Lua常用應用程序和框架
在本教程中,將讓您了解Lua可以執行的操作,并了解有關其安裝和使用的更多信息,請參閱kepler網站。
Orbit是Lua的MVC Web框架。 它完全放棄了CGILua“腳本”模型,轉而支持應用程序,每個Orbit應用程序都可以放在一個文件中,但如果需要,可以將它分成多個文件。
所有Orbit應用程序都遵循WSAPI協議,因此它們目前與Xavante,CGI和Fastcgi一起使用。 它包含一個啟動程序,可以輕松啟動Xavante實例進行開發。
安裝Orbit的最簡單方法是使用LuaRocks。 Luarocks是安裝Orbit的安裝的命令。 為此,需要先安裝LuaRocks。
如果尚未安裝所有依賴項,則以下是在Unix/Linux 環境中設置Orbit時要遵循的步驟。
安裝Apache 連接到服務器,使用以下命令安裝Apache2及其支持模塊并啟用所需的Apache2模塊 -
$ sudo apt-get install apache2 libapache2-mod-fcgid libfcgi-dev build-essential
$ sudo a2enmod rewrite
$ sudo a2enmod fcgid
$ sudo /etc/init.d/apache2 force-reload
安裝LuaRocks
$ sudo apt-get install luarocks
安裝WSAPI,FCGI,Orbit和Xavante
$ sudo luarocks install orbit
$ sudo luarocks install wsapi-xavante
$ sudo luarocks install wsapi-fcgi
設置Apache2
$ sudo raj /etc/apache2/sites-available/default
在配置文件的<Directory /var/www/>部分下面添加以下部分。 如果此部分有AllowOverride None,則需要將None更改為All,以便.htaccess文件可以在本地覆蓋配置。
<IfModule mod_fcgid.c>
AddHandler fcgid-script .lua
AddHandler fcgid-script .ws
AddHandler fcgid-script .op
FCGIWrapper "/usr/local/bin/wsapi.fcgi" .ws
FCGIWrapper "/usr/local/bin/wsapi.fcgi" .lua
FCGIWrapper "/usr/local/bin/op.fcgi" .op
#FCGIServer "/usr/local/bin/wsapi.fcgi" -idle-timeout 60 -processes 1
#IdleTimeout 60
#ProcessLifeTime 60
</IfModule>
重新啟動服務器以確保所做的更改生效。 要啟用應用程序,需要將+ExecCGI添加到Orbit應用程序根目錄中的.htaccess文件中 - 在本例中目錄為/var/www。
Options +ExecCGI
DirectoryIndex index.ws
Orbit簡單示例 -
#!/usr/bin/env index.lua
-- index.lua
require"orbit"
-- declaration
module("myorbit", package.seeall, orbit.new)
-- handler
function index(web)
return my_home_page()
end
-- dispatch
myorbit:dispatch_get(index, "/", "/index")
-- Sample page
function my_home_page()
end
現在,可以啟動Web瀏覽器了。 轉到http://localhost:8080/,應該看到以下輸出 -
First Page
Orbit提供了另一種選擇,即Lua代碼可以生成html。
#!/usr/bin/env index.lua
-- index.lua
require"orbit"
function generate()
return html {
head{title "HTML Example"},
body{
h2{"Here we go again!"}
}
}
end
orbit.htmlify(generate)
print(generate())
創建表單
一個簡單的表單示例,如下所示 -
#!/usr/bin/env index.lua
require"orbit"
function wrap (inner)
return html{ head(), body(inner) }
end
function test ()
return wrap(form (H'table' {
tr{td"First name",td( input{type='text', name='first'})},
tr{td"Second name",td(input{type='text', name='second'})},
tr{ td(input{type='submit', value='Submit!'}),
td(input{type='submit',value='Cancel'})
},
}))
end
orbit.htmlify(wrap,test)
print(test())
如前所述,WSAPI充當許多項目的基礎,并在其中嵌入了多個功能。 可以使用WSAPI并支持以下平臺 -
WSAPI支持的服務器和接口包括 -
WSAPI提供了許多庫,可以更輕松地使用Lua進行Web編程。 Lua中的一些受支持的功能包括,
WSAPI的一個簡單示例,如下所示 -
#!/usr/bin/env wsapi.cgi
module(..., package.seeall)
function run(wsapi_env)
local headers={ ["Content-type"]="text/html" }
local function hello_text()
coroutine.yield("<html><body>")
coroutine.yield("<p>Hello Wsapi - by yiibai.com !</p>")
coroutine.yield("<p>PATH_INFO: " .. wsapi_env.PATH_INFO .. "</p>")
coroutine.yield("<p>SCRIPT_NAME: " .. wsapi_env.SCRIPT_NAME .. "</p>")
coroutine.yield("</body></html>")
end
return 200, headers, coroutine.wrap(hello_text)
end
在上面的代碼中看到形成并返回一個簡單的html 頁面??梢钥吹絽f同程序的用法,它可以將語句返回到調用函數。 最后,返回html狀態代碼(200),標題和html頁面。
Xavante是一個Lua HTTP 1.1 Web服務器,它使用基于URI映射處理程序的模塊化體系結構。 Xavante目前提供以下功能 -
文件處理程序用于常規文件。重定向處理程序啟用URI重映射和WSAPI處理程序,以便與WSAPI應用程序一起處理。
一個簡單的例子,如下所示 -
require "xavante.filehandler"
require "xavante.cgiluahandler"
require "xavante.redirecthandler"
-- Define here where Xavante HTTP documents scripts are located
local webDir=XAVANTE_WEB
local simplerules={
{ -- URI remapping example
match="^[^%./]*/$",
with=xavante.redirecthandler,
params={"index.lp"}
},
{ -- cgiluahandler example
match={"%.lp$", "%.lp/.*$", "%.lua$", "%.lua/.*$" },
with=xavante.cgiluahandler.makeHandler (webDir)
},
{ -- filehandler example
match=".",
with=xavante.filehandler,
params={baseDir=webDir}
},
}
xavante.HTTP{
server={host="*", port=8080},
defaultHost={
rules=simplerules
},
}
要使用Xavante的虛擬主機,對xavante.HTTP的調用將更改為如下所示 -
xavante.HTTP{
server={host="*", port=8080},
defaultHost={},
virtualhosts={
["www.yiibai.com"]=simplerules
}
}
有許多基于Lua的Web框架和組件可供使用,根據需要選擇它。 還有其他可用的Web框架,包括以下內容 -
這些Web框架可在Web應用程序中充分利用,以執行強大的操作功能。
對于簡單的數據操作,可使用文件處理。但有時,這些文件操作可能效率不高,可擴展且功能強大。 為此經常需要使用數據庫。 LuaSQL是一個從Lua到許多數據庫管理系統的簡單接口。 LuaSQL是一個庫,它為不同類型的SQL提供支持。 包括 -
在本教程中,將介紹在Lua中如何使用MySQL和SQLite數據庫操作處理。它為兩者使用通用接口,并且也可以將此實現移植到其他類型的數據庫。 首先來看看如何在MySQL中進行操作。
要使以下示例按預期工作,需要初始數據庫安裝設置。 假設如下 -
假設Lua實現正確完成,使用一個簡單的require語句來導入sqlite庫。
mysql=require "luasql.mysql"
變量mysql將通過引用主mysql表來提供對函數的訪問。
通過啟動MySQL環境,然后為環境創建連接。 如下所示 -
local env=mysql.mysql()
local conn=env:connect('test','root','123456')
上述連接將連接到現有的MySQL文件,并與新創建的文件建立連接。
連接提供了一個簡單的執行函數,可以幫助我們執行創建,插入,刪除,更新等操作完成所有數據庫操作。 語法如下所示 -
conn:execute([[ 'MySQLSTATEMENT' ]])
在上面的語法中,需要確保conn是打開的,以及MySQL連接成功,并用正確的SQL語句替換'MySQLSTATEMENT'。
創建表示例
下面顯示了一個簡單的create table示例。 它創建一個表,其中包含兩個字段參數: id類型為integer,name類型為varchar。
mysql=require "luasql.mysql"
local env=mysql.mysql()
local conn=env:connect('test','root','123456')
print(env,conn)
status,errorString=conn:execute([[CREATE TABLE sample2 (id INTEGER, name TEXT);]])
print(status,errorString )
運行上述程序時,將創建一個名為sample的表,其中包含兩列,即id和name。
MySQL environment (004BB178) MySQL connection (004BE3C8)
0 nil
如果語句有任何錯誤,將返回錯誤語句而不是nil。 一個簡單的錯誤聲明如下所示 -
LuaSQL: Error executing query. MySQL: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"id INTEGER, name TEXT)' at line 1
插入語句示例 MySQL的insert語句如下所示-
conn:execute([[INSERT INTO sample values('11','Maxsu')]])
更新語句示例 MySQL的更新語句如下所示 -
conn:execute([[UPDATE sample3 SET name='Yiibai' where id='12']])
刪除語句示例 MySQL的刪除語句如下所示 -
conn:execute([[DELETE from sample3 where id='12']])
選擇語句示例 就select語句而言,我們需要循環遍歷每一行并提取所需的數據。一個簡單的選擇語句如下所示 -
cursor,errorString=conn:execute([[select * from sample]])
row=cursor:fetch ({}, "a")
while row do
print(string.format("Id: %s, Name: %s", row.id, row.name))
-- reusing the table of results
row=cursor:fetch (row, "a")
end
在上面的代碼中,conn是一個打開的MySQL連接。 在execute語句返回的游標的幫助下,可以遍歷表響應并獲取所需的選擇數據。
下面給出了包括所有上述語句的完整示例 -
mysql=require "luasql.mysql"
local env=mysql.mysql()
local conn=env:connect('test','root','123456')
print(env,conn)
status,errorString=conn:execute([[CREATE TABLE sample3 (id INTEGER, name TEXT)]])
print(status,errorString )
status,errorString=conn:execute([[INSERT INTO sample3 values('12','Maxsu')]])
print(status,errorString )
cursor,errorString=conn:execute([[select * from sample3]])
print(cursor,errorString)
row=cursor:fetch ({}, "a")
while row do
print(string.format("Id: %s, Name: %s", row.id, row.name))
row=cursor:fetch (row, "a")
end
-- close everything
cursor:close()
conn:close()
env:close()
運行上述程序時,將獲得以下輸出 -
MySQL environment (0037B178) MySQL connection (0037EBA8)
0 nil
1 nil
MySQL cursor (003778A8) nil
Id: 12, Name: Maxsu
事務是一種確保數據一致性的機制。事務具有以下四個屬性 -
事務以START TRANSACTION開始; 并以commit或rollback語句結束。
開始事務
要啟動事務,需要在Lua中執行以下語句,假設conn是一個打開的MySQL連接。
conn:execute([[START TRANSACTION;]])
回滾事務
需要執行以下語句來回滾執行啟動事務后所做的更改。
conn:execute([[ROLLBACK;]])
提交事務
需要執行以下語句來提交啟動事務執行后所做的更改。
conn:execute([[COMMIT;]])
在上面和后面的章節中已經了解了MySQL的基本SQL操作。 記住事務,雖然這里沒有為SQLite3解釋,但同樣的語句也適用于SQLite3。
Lua由于其簡單的語言結構和語法,它廣泛地用于游戲引擎開發中。 垃圾收集功能通常在由于使用豐富的圖形而消耗大量內存的游戲中非常有用。 一些使用Lua的游戲引擎包括 -
這些游戲引擎中的每一個都基于Lua,并且每個引擎中都有一組豐富的API。 接下來將簡要介紹每種引擎功能。
Corona SDK是一款支持iPhone,iPad和Android平臺的跨平臺移動游戲引擎。 有一個免費版本的Corona SDK,可用于功能有限的小游戲。可以在需要時升級到其他版本。
Corona SDK提供了許多功能,包括以下功能 -
使用上述API開發應用程序比使用iOS和Android單獨使用本機API更容易,更快捷。
Gideros提供跨平臺SDK來為iOS和Android創建游戲,可以免費使用Gideros。 Gideoros的一些顯著優勢包括 -
ShiVa3D是3D游戲引擎之一,它提供了一個圖形編輯器,旨在為Web,控制臺和移動設備創建應用程序和視頻游戲。 它支持多種平臺,包括Windows,Mac,Linux,iOS,Android,BlackBerry,Palm OS,Wii和WebOS。
一些主要功能包括 -
Shiva3d的網絡版是完全免費的,其他版本可能需要收費。
Moai SDK是一款支持iPhone,iPad和Android平臺的跨平臺移動游戲引擎。 Moai平臺最初由Moai SDK(一種開源游戲引擎)和Moai Cloud(一種用于托管和部署游戲服務的服務的云平臺)組成。 現在Moai Cloud已關閉,只有游戲引擎可用。
Moai SDK可在多種平臺上運行,包括iOS,Android,Chrome,Windows,Mac和Linux。
LOVE是一個可用于制作2D游戲的框架,它是免費和開源的。 它支持Windows,Mac OS X和Linux平臺。
它提供了多種功能,包括:
CryEngine是由德國游戲開發商Crytek開發的游戲引擎。 它已從第1代發展到第4代,是一種先進的開發解決方案。 它支持PC,Xbox 360,PlayStation3和WiiU游戲。
它提供了多種功能,包括:
這些游戲SDK/框架中的每一個都有各自的優缺點。 在它們之間進行適當的選擇可以讓工作更輕松,并且可以更好地利用它。 因此,在使用它之前,需要了解游戲的要求,然后分析哪些能滿足需求,然后才能決定使用哪一個。
Lua標準庫提供了一組豐富的函數,這些函數直接使用C語言API實現,并使用Lua編程語言構建。這些庫提供Lua編程語言中的服務以及文件和數據庫操作之外的服務。
這些在官方C語言API中構建的標準庫作為單獨的C模塊提供。它包括以下內容 -
我們在整個教程中使用了各種主題下的基本庫。 下表提供了相關頁面的鏈接,并列出了本Lua教程各部分所涵蓋的功能。
編號 | 庫/方法 | 作用 |
1 | 錯誤處理 | 包括錯誤處理函數,如斷言, 錯誤,如Lua錯誤處理中所述。 |
2 | 內存管理 | 包括與垃圾收集相關的自動內存管理功能, 如Lua垃圾收集中所述。 |
3 | dofile ([filename]) | 它打開文件并以塊的形式執行文件的內容。 |
4 | _G | 因此是保存全局環境的全局變量(即_G._G=_G)。 |
5 | getfenv ([f]) | 返回函數使用的當前環境。 |
6 | getmetatable (object) | 如果object沒有metatable, 則返回nil。 否則,如果object的metatable具有__metatable字段, |
7 | ipairs (t) | 此函數獲取表的索引和值。 |
8 | load (func [, chunkname]) | 使用函數func加載一個塊來獲取它的碎片。 |
9 | loadfile ([filename])) | 與load類似,但是如果沒有給出文件名,則從文件filename或標準輸入中獲取塊。 |
10 | loadstring (string [, chunkname]) | 與load函數類似,但從給定的字符串中獲取塊。 |
11 | next (table [, index]) | 允許程序遍歷表的所有字段。 |
12 | pairs (t) | 暫停正在運行的協同程序。 |
13 | print (...) | 打印給定的參數值。 |
14 | rawequal (v1, v2) | 檢查v1是否等于v2,而不調用任何無方法。 返回一個布爾值。 |
15 | rawget (table, index) | 獲取table [index]的值, 而不調用任何方法。table必須是表; index可以是任何值。 |
16 | rawset (table, index, value) | 將table [index]的值設置為value,而不調用任何方法。 |
17 | select (index, ...) | 如果index是數字,則返回參數編號索引后的所有參數。 |
18 | setfenv (f, table) | 設置給定函數使用的環境。 |
19 | setmetatable (table, metatable) | 設置給定表的元表。 |
20 | tonumber (e [, base]) | 嘗試將參數轉換為數字。 |
21 | tostring (e) | 接收任何類型的參數并將其轉換為合理格式的字符串。 |
22 | type (v) | 返回唯一參數的類型,編碼為字符串。 |
23 | unpack (list [, i [, j]]) | 返回給定表中的元素。 |
24 | _VERSION | 包含當前解釋器版本的字符串的全局變量(不是函數)。 |
25 | 協同程序 | 包括Lua協同程序中解釋的協程操作功能。 |
模塊庫提供了在Lua中加載模塊的基本功能。 它直接在全局環境中導出一個函數:require。 其他所有內容都在表包中導出。 有關模塊庫的詳細信息,請參見前面的Lua模塊教程。
Lua提供了豐富的字符串操作功能。 前面的Lua字符串教程詳細中介紹了這一點。
Lua幾乎在其所有操作中都依賴于表。 前面的Lua表教程詳中細介紹了這一點。
經常需要編程中的數據存儲工具,這是由Lua中的文件I/O的標準庫函數提供的。 它在前面的Lua 文件操作教程中討論過。
Lua提供了一個調試庫,它提供了創建自己的調試器的所有原始函數。 在前面的Lua調試教程中討論過了。
經常需要在科學和工程計算中進行數學運算,可以使用標準的Lua庫數學來實現。 數學庫中可用的函數列表如下表所示 -
編號 | 庫或方法 | 描述 |
1 | math.abs(x) | 返回x的絕對值。 |
2 | math.acos(x) | 返回x的弧余弦值(以弧度表示)。 |
3 | math.asin(x) | 返回x的弧正弦(以弧度表示)。 |
4 | math.atan(x) | 返回x的反正切(以弧度表示)。 |
5 | math.atan2(y,x) | 返回y / x的反正切(以弧度表示),但使用兩個參數的符號來查找結果的象限(它也正確處理x為零的情況。) |
6 | math.ceil(x) | 返回大于或等于x的最小整數。 |
7 | math.cos(x) | 返回x的余弦值(假設為弧度)。 |
8 | math.cosh(x) | 返回x的雙曲余弦值。 |
9 | math.deg(x) | 以度為單位返回角度x(以弧度表示)。 |
10 | math.exp(x) | 返回值e的x次冪。 |
11 | math.floor(x) | 返回小于或等于x的最大整數。 |
12 | math.fmod(x,y) | 返回x除以y的余數,將商舍入為零。 |
13 | math.frexp(x) | 返回m和e,使得x=m2e,e是整數,m的絕對值在[0.5,1]范圍內(或者當x為零時為零)。 |
14 | math.huge | HUGE_VAL值是一個大于或等于任何其他數值的值。 |
15 | math.ldexp(m, e) | 返回m2e(e是一個整數)。 |
16 | math.log(x) | 返回x的自然對數。 |
17 | math.log10(x) | 返回x的以10為底的對數。 |
18 | math.max(x,...) | 返回參數中的最大值。 |
19 | math.min(x,...) | 返回參數中的最小值。 |
20 | math.modf(x) | 返回兩個數字,x的整數部分和x的小數部分。 |
21 | math.pi | pi的值。 |
22 | math.pow(x,y) | 返回x的y方。(也可以使用表達式x ^ y來計算此值。) |
23 | math.rad(x) | 以弧度為單位返回角度x(以度為單位)。 |
24 | math.random([m [, n]]) | 此函數是ANSI C提供的簡單偽隨機生成器函數rand的接口。 |
25 | math.randomseed(x) | 將x設置為偽隨機生成器的“種子”:相等的種子產生相等的數字序列。 |
26 | math.sin(x) | 返回x的正弦值(假設為弧度)。 |
27 | math.sinh(x) | 返回x的雙曲正弦值。 |
28 | math.sqrt(x) | 返回x的平方根。(也可以使用表達式x ^ 0.5來計算此值。) |
29 | math.tan(x) | 返回x的正切(假設為弧度)。 |
30 | math.tanh(x) | 返回x的雙曲正切值。 |
使用三角函數的簡單示例如下所示-
radianVal=math.rad(math.pi / 2)
io.write(radianVal,"\n")
-- Sin value of 90(math.pi / 2) degrees
io.write(string.format("%.1f ", math.sin(radianVal)),"\n")
-- Cos value of 90(math.pi / 2) degrees
io.write(string.format("%.1f ", math.cos(radianVal)),"\n")
-- Tan value of 90(math.pi / 2) degrees
io.write(string.format("%.1f ", math.tan(radianVal)),"\n")
-- Cosh value of 90(math.pi / 2) degrees
io.write(string.format("%.1f ", math.cosh(radianVal)),"\n")
-- Pi Value in degrees
io.write(math.deg(math.pi),"\n")
當運行上面的程序時,將得到以下輸出 -
0.027415567780804
0.0
1.0
0.0
1.0
180
使用常見數學函數的簡單示例如下所示-
-- Floor
io.write("Floor of 10.5055 is ", math.floor(10.5055),"\n")
-- Ceil
io.write("Ceil of 10.5055 is ", math.ceil(10.5055),"\n")
-- Square root
io.write("Square root of 16 is ",math.sqrt(16),"\n")
-- Power
io.write("10 power 2 is ",math.pow(10,2),"\n")
io.write("100 power 0.5 is ",math.pow(100,0.5),"\n")
-- Absolute
io.write("Absolute value of -10 is ",math.abs(-10),"\n")
--Random
math.randomseed(os.time())
io.write("Random number between 1 and 100 is ",math.random(),"\n")
--Random between 1 to 100
io.write("Random number between 1 and 100 is ",math.random(1,100),"\n")
--Max
io.write("Maximum in the input array is ",math.max(1,100,101,99,999),"\n")
--Min
io.write("Minimum in the input array is ",math.min(1,100,101,99,999),"\n")
當運行上面的程序時,將得到以下輸出 -
Floor of 10.5055 is 10
Ceil of 10.5055 is 11
Square root of 16 is 4
10 power 2 is 100
100 power 0.5 is 10
Absolute value of -10 is 10
Random number between 1 and 100 is 0.22876674703207
Random number between 1 and 100 is 7
Maximum in the input array is 999
Minimum in the input array is 1
上面的例子只是一些常見的例子,可以根據需要使用數學庫,所以嘗試使用所有的函數以更熟悉運用。
在任何應用程序中,通常都需要訪問操作系統級功能,并且可以使用操作系統庫。 可用功能列表如下表所示。
編號 | 庫或方法 | 描述 |
1 | os.clock() | 返回程序使用的CPU時間(以秒為單位)的近似值。 |
2 | os.date([format[, time]]) | 返回包含日期和時間的字符串或表,根據給定的字符串格式進行格式化。 |
3 | os.difftime(t2,t1) | 返回從時間t1到時間t2的秒數。在POSIX,Windows和其他一些系統中,恰好是t2-t1的值。 |
4 | os.execute([command]) | 此功能相當于ANSI C功能系統。 它傳遞要由操作系統shell執行的命令。 如果命令成功終止,則第一個結果為true,否則為nil。 |
5 | os.exit([code[, close]) | 調用ANSI C函數出口以終止宿主程序。 如果code為true,則返回狀態為EXIT_SUCCESS; 如果code為false,則返回狀態為EXIT_FAILURE; 如果code是數字,則返回的狀態是此數字。 |
6 | os.getenv(varname) | 返回進程環境變量varname的值,如果未定義變量,則返回nil。 |
7 | os.remove(filename) | 使用給定名稱刪除文件(或POSIX系統上的空目錄)。 如果此函數失敗,則返回nil,以及描述錯誤和錯誤代碼的字符串。 |
8 | os.rename(oldname, newname) | 將名為oldname的文件或目錄重命名為newname。 如果此函數失敗,則返回nil,以及描述錯誤和錯誤代碼的字符串。 |
9 | os.setlocale(locale [,category]) | 設置程序的當前區域設置。 locale是一個依賴于系統的字符串,用于指定語言環境; category是一個可選字符串,用于描述要更改的類別:all,collate,ctype,currency,numeric或time; 默認類別(category)是"all"。該函數返回新語言環境的名稱,如果無法滿足請求,則返回nil。 |
10 | os.time([table]) | 返回不帶參數調用的當前時間,或表示給定表指定的日期和時間的時間。 此表必須包含字段年,月和日,并且可能包含字段小時(默認值為12),分鐘(默認值為0),秒(默認值為0)和isdst(默認值為nil)。 有關這些字段的說明,請參見os.date函數。 |
11 | os.tmpname() | 返回一個文件名,該文件名可用于臨時文件。 文件必須在使用前顯式打開,并在不再需要時顯式刪除。 |
使用常見數學函數的簡單示例如下所示 -
-- Date with format
io.write("The date is ", os.date("%m/%d/%Y"),"\n")
-- Date and time
io.write("The date and time is ", os.date(),"\n")
-- Time
io.write("The OS time is ", os.time(),"\n")
-- Wait for some time
for i=1,1000000 do
end
-- Time since Lua started
io.write("Lua started before ", os.clock(),"\n")
當運行上面的程序時,將得到類似的輸出如下 -
The date is 01/25/2018
The date and time is 01/25/18 07:38:40
The OS time is 1490615720
Lua started before 0.013
上面的例子只是一些常見的例子,可根據自己的需要使用OS庫,建議嘗試使用所有的功能以便更加熟悉。像remove這樣的函數有助于刪除文件,執行有助于于執行OS命令。
http://stevedonovan.github.io/ldoc/manual/doc.md.html
LDOC 入門指南 -- 重點是里面的安裝 - ZhYQ_note - 博客園
LDoc遵循JavaDoc等文檔生成工具的傳統,即只有doc comment可以被解析,這種doc comment必須以三個-開頭
brew install luarocks -v
luarocks install Ldoc
很多好學的同學都喜歡在業余時間充電,與其蹲廁所看垃圾八卦新聞,還不如學習的有用的知識充實自己,LUA是一種比較簡單 的計算機軟件語言,下面一些簡易知識可以幫助你輕松入門。
一、lua編寫和調試
Lua是一個免費開源的語言,官網是這個http://www.lua.org/download.html,可以到這里下載源碼或者編譯好的版本。
然后編輯器(IDE),可以用Notepad++,或者Sublime,Java碼農可以用eclipse的插件Luaclipse,c#碼農可以用vs。
本人需要用N個電腦,經常有一點簡單的代碼想調試一下,又懶得搞環境,就用在線運行環境,這個算是比較好用的一個http://www.dooccn.com/lua,推薦使用。
二、lua語法
1、Lua數據類型
nil
boolean:布爾值, 也就是true/false。對于數字的真假判斷跟其他語言有一點點不一樣的,0為真(其他語言里面0一般為假)
number
string
userdata
function:函數
thread
table:lua里面沒有array和map的概念,都用map來表示,這是lua特有的一個類型,下面會重點介紹一下
使用type函數可以查看某個變量的類型,如:
print(type("Hello World")) --> stringprint(type(10.4*3)) --> numberprint(type(print)) --> functionprint(type(type(X))) --> string
2、Lua table類型的使用
table可以當做array來使用,但是不要求子元素類型一致
local days={}--這里聲明了一個table,變量名是days,local代表局部變量 days[1]="Sunday"--請注意,正常語言都是從0開始,lua的下標是從1開始 days[2]="Monday"days[3]=20--注意這里,正常語言的array都要求元素的類型是一直的,lua沒這個限制 print(days[1])--print函數是把變量打印到控制臺,如果在wow聊天框運行,就打印到聊天框 --lua作為array使用,并不用提前指定的他的長度,這點還是挺爽的
table可以當做map來使用,保存key/value
local a={}a["x"]=10a["y"]=20--或者這樣寫,效果是一樣的 local a={} a={ ["x"]=10, ["y"]=20}print(a["x"])--使用方括號來訪問keyprint(a.x)--直接.也是可以的,這種方式比較常用,畢竟簡單啊
甚至可以嵌套
local inner={} inner[1]=10inner[2]="test"inner.zz="sleep"local outer={}outer.x=innerouter.y=2print(outer.x.zz)
求table的size,用#號就可以了
local a={1,2,"a"}print(#a) -->3
3、加減乘除冪模、邏輯運算
1)表達式:
+(加)
-(減)
*(乘)
/(除)
^(冪)
%(取模)
2)邏輯運算符
and or not
print(true and true)-->trueprint(false and true)--falseprint(true or false)-->trueprint(not 1)--falseprint(not 0)-->false,前面說過了,0也是真,別忘了 --基本跟其他語言沒有任何區別,不廢話了
4、關系運算
==等于~=不等于,前面那個是波浪線 >大于=大于等于<小于 <=小于等于
5、局部變量和全局變量
do a=1--不帶local就是全局變量 local b=2--帶local就是局部變量 endprint(a)-->1print(b)-->nil
6、if/else
if true thenelseif true thenelseend
7、while循環
local i=1while a[i] do print(a[i]) i=i + 1end
8、for循環
for i=1, 3 do print(i)endfor k, v in pairs(t) do print(k, v)endfor i, v in ipairs(t) do print(i, v)end
9、repeat..until
repeat line=io.read()until line ~=''print(line)
10、goto
goto quit print('come on')::quit::print('quit') --這里只輸出quit
10、break、return
break是結束循環,return是返回,lua不支持continue
11、注釋
上面我們已經使用了較多的注釋,這里重復一下
--單行注釋local a--[[ 多行注釋 local a ]]--local b
12、函數
來看一下常規的,求兩個數字中最大的
--[[ 函數返回兩個值的最大值 --]]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的函數可以返回多個值
function GetPlayerPosition(unit) if unit=="player" then return 1,2,3 endend--返回自己的坐標local x,y,z=GetPlayerPosition("player")print(x)print(y)print(z)print(x,y,z)--print也支持傳多個參數,一次性全打印出來,中間用空格分隔
沒有名字的函數叫“匿名函數”,效果跟上面一樣的
--返回自己的坐標local x,y,z=function(unit) if unit=="player" then return 1,2,3 endendprint(x,y,z)
lua的函數可以接收任意個數的參數
*請認真填寫需求信息,我們會在24小時內與您取得聯系。