Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 久久一区视频,天堂中文字幕在线观看,天天干天天干天天操

          整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          Nginx Lua編程基礎(chǔ)

          Nginx Lua編程基礎(chǔ)

          ua是一門腳本動態(tài)語言,并不太適合做復雜業(yè)務(wù)邏輯的程序開發(fā),但是,在高并發(fā)場景下,Nginx Lua編程是解決性能問題的利器。

          Nginx Lua編程主要的應用場景如下:

          • API網(wǎng)關(guān):實現(xiàn)數(shù)據(jù)校驗前置、請求過濾、API請求聚合、AB測試、灰度發(fā)布、降級、監(jiān)控等功能,著名的開源網(wǎng)關(guān)Kong就是基于Nginx Lua開發(fā)的。
          • 高速緩存:可以對響應內(nèi)容進行緩存,減少到后端的請求,從而提升性能。比如,Nginx Lua可以和Java容器、Redis整合,由Java容器進行業(yè)務(wù)處理和數(shù)據(jù)緩存,而Nginx負責讀緩存并進行響應,從而解決Java容器的性能瓶頸
          • 簡單的動態(tài)Web應用:可以完成一些業(yè)務(wù)邏輯處理較少但耗費CPU的簡單應用,比如模板頁面的渲染。一般的Nginx Lua頁面渲染處理流程為:從Redis獲取業(yè)務(wù)處理結(jié)果數(shù)據(jù),從本地加載XML/HTML頁面模板,然后進行頁面渲染。
          • 網(wǎng)關(guān)限流:緩存、降級、限流是解決高并發(fā)的三大利器,Nginx內(nèi)置了令牌限流的算法,但是對于分布式的限流場景,可以通過Nginx Lua編程定制自己的限流機制

          ngx_lua是Nginx的一個擴展模塊,將Lua VM嵌入Nginx,請求時創(chuàng)建一個VM,請求結(jié)束時回收VM,這樣就可以在Nginx內(nèi)部運行Lua腳本,使得Nginx變成一個Web容器。以O(shè)penResty為例,其提供了一些常用的ngx_lua開發(fā)模塊:

          • lua-resty-memcached:通過Lua操作memcache
          • lua-resty-mysql:通過Lua操作MySQL
          • lua-resty-redis:通過Lua操作Redis緩存
          • lua-resty-dns:通過Lua操作DNS域名服務(wù)器
          • lua-resty-limit-traffic:通過Lua進行限流
          • lua-resty-template:通過Lua進行模板渲染
          • lua-resty-jwt:通過Lua生成jwt
          • lua-resty-kafka:通過Lua操作kafka

          Lua腳本需要通過Lua解釋器來解釋執(zhí)行,除了Lua官方的默認解釋器外,目前使用廣泛的Lua解釋器叫做LuaJIT。LuaJIT采用C語言編寫,被設(shè)計成全兼容標準Lua 5.1,因此LuaJIT代碼的語法和標準Lua的語法沒多大區(qū)別。但是LuaJIT的運行速度比標準Lua快數(shù)十倍。

          Nginx Lua的執(zhí)行原理

          在OpenResty中,每個Worker進程使用一個Lua VM,當請求被分配到Worker時,將在這個Lua VM中創(chuàng)建一個協(xié)程,協(xié)程之間數(shù)據(jù)隔離,每個協(xié)程都具有獨立的全局變量。

          ngx_lua是將Lua VM嵌入Nginx,讓Nginx執(zhí)行Lua腳本,并且高并發(fā)、非阻塞地處理各種請求Lua內(nèi)置協(xié)程可以很好地將異步回調(diào)轉(zhuǎn)換成順序調(diào)用的形式。ngx_lua在Lua中進行的IO操作都會委托給Nginx的事件模型,從而實現(xiàn)非阻塞調(diào)用。開發(fā)者可以采用串行的方式編寫程序,ngx_lua會在進行阻塞的IO操作時自動中斷,保存上下文,然后將IO操作委托給Nginx事件處理機制,在IO操作完成后,ngx_lua會恢復上下文,程序繼續(xù)執(zhí)行,這些操作對用戶程序都是透明的。

          每個Worker進程都持有一個Lua解釋器或LuaJIT實例,被這個Worker處理的所有請求共享這個實例。每個請求的context上下文會被Lua輕量級的協(xié)程分隔,從而保證每個請求是獨立的。

          ngx_lua采用one-coroutine-per-request的處理模型,對于每個用戶請求,ngx_lua會喚醒一個協(xié)程用于執(zhí)行用戶代碼處理請求,當請求處理完成后,這個協(xié)程會被銷毀。每個協(xié)程都有一個獨立的全局環(huán)境,繼承于全局共享的、只讀的公共數(shù)據(jù)。所以,被用戶代碼注入全局空間的任何變量都不會影響其他請求的處理,并且這些變量在請求處理完成后會被釋放,這樣就保證所有的用戶代碼都運行在一個sandbox(沙箱)中,這個沙箱與請求具有相同的生命周期。

          得益于Lua協(xié)程的支持,ngx_lua在處理10000個并發(fā)請求時,只需要很少的內(nèi)存。根據(jù)測試,ngx_lua處理每個請求只需要2KB的內(nèi)存,如果使用LuaJIT就會更少

          Nginx Lua配置指令

          ngx_lua定義的Nginx配置指令大致如下:

          • lua_package_path:配置Lua外部庫的搜索路徑,搜索的文件類型為.lua。
          • lua_package_cpath:配置Lua外部搜索庫的搜索路徑,搜索C語言編寫的外部庫文件。
          • init_by_lua:Master進程啟動時掛載的Lua代碼塊,常用于導入公共模塊。
          • init_by_lua_file:Master進程啟動時掛載的Lua腳本文件。
          • init_worker_by_lua:Worker進程啟動時掛載的Lua代碼塊,常用于執(zhí)行一些定時任務(wù)
          • init_worker_by_lua_file:Worker進程啟動時掛載的Lua文件,常用于執(zhí)行一些定時任務(wù)
          • set_by_lua:類似于rewrite模塊的set指令,將Lua代碼塊的返回結(jié)果設(shè)置在Nginx的變量中。
          • set_by_lua_file:同上,執(zhí)行的是腳本Lua腳本文件。
          • rewrite_by_lua:執(zhí)行在rewrite階段的Lua代碼塊,完成轉(zhuǎn)發(fā)、重定向、緩存等功能。
          • rewrite_by_lua_file:同上,執(zhí)行的是Lua腳本文件。
          • access_by_lua:執(zhí)行在access階段的Lua代碼塊,完成IP準入、接口權(quán)限等功能。
          • access_by_lua_file:同上,執(zhí)行的是Lua腳本文件。
          • content_by_lua:執(zhí)行在content階段的Lua代碼塊,執(zhí)行結(jié)果將作為請求響應的內(nèi)容。
          • content_by_lua_file:同上,執(zhí)行的是Lua腳本文件。
          • content_by_lua_block:content_by_lua的升級款,在一對花括號中編寫Lua代碼,而不需要做特殊字符轉(zhuǎn)譯。
          • header_filter_by_lua:響應頭部過濾處理的Lua代碼塊,可以用于添加設(shè)置響應頭部信息,如Cookie相關(guān)屬性。
          • body_filter_by_lua:響應體過濾處理的Lua代碼塊,例如加密響應體。
          • log_by_lua:異步完成日志記錄的Lua代碼塊,例如既可以在本地記錄日志,也可以記錄到ETL集群。

          ngx_lua配置指令在Nginx的HTTP請求處理階段所處的位置如圖:

          常用配置指令

          • lua_package_path指令:用于設(shè)置".lua"外部庫的搜索路徑,此指令的上下文為http配置塊,默認值為LUA_PATH環(huán)境變量內(nèi)容或者lua編譯的默認值。
            • 格式:lua_package_path lua-style-path-str。
            • lua_package_cpath指令:用于設(shè)置Lua的C語言塊外部庫".so"(Linux)或".dll"(Windows)的搜索路徑,此指令的上下文為http配置塊。
            • 格式:lua_package_cpath lua-style-cpath-str
          http {
            ...
            #設(shè)置“.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}獲取虛擬服務(wù)器server的前綴路徑,server的前綴路徑通常在Nginx服務(wù)器啟動時通過-p PATH命令在指定。

          • init_by_lua指令:只能用于http上下文,運行在配置加載階段。當Nginx的master進程在加載Nginx配置文件時,在全局Lua VM級別上運行由參數(shù)lua-script-str指定的Lua腳本塊。若使用init_by_lua_file指令,后面跟lua文件的路徑( lua_file_path),則在全局Lua VM 級別上運行l(wèi)ua_file_path文件指定的lua腳本。如果Lua腳本的緩存是關(guān)閉的,那么每一次請求都運行一次init_by_lua處理程序。

          格式為:init_by_lua lua-script-str。

          • lua_load_cache指令:用于啟用或禁止Lua腳本緩存。可以使用的上下文為http、server、location配置塊。默認開啟。

          格式為:lua_code_cache on | off

          http {
            ...
          	#項目初始化
            init_by_lua_file	conf/luaScript/initial/loading_config.lua;
            	
            #調(diào)試模式,關(guān)閉lua腳本緩存
            lua_code_cache on;
            ...
          }

          在緩存關(guān)閉的時,set_by_lua_file、content_by_lua_file、access_by_lua_file、content_by_lua_file等指令中引用的Lua腳本都將不會被緩存,所有的Lua腳本都將從頭開始加載。

          • set_by_lua指令:將Lua腳本塊的返回結(jié)果設(shè)置在Nginx變量中。

          格式為:set_by_lua $destVar lua-script-str params

          location /set_by_lua_demo {
          	#set 指令定義兩個Nginx變量
            set $foo 1;
            set $bar 2;
            			
            #調(diào)用Lua內(nèi)聯(lián)代碼,將結(jié)果放入Nginx變量$sum
            #Lua腳本的含義是,將兩個輸入?yún)?shù)$foo、$bar累積起來,然后相加的結(jié)果設(shè)置Nginx變量$sum中
            set_by_lua $sum 'return tonumber(ngx.arg[1]) + tonumber(ngx.arg[2])' $foo $bar;
            
            echo "$foo + $bar=$sum";
          }

          運行結(jié)果:

          ?  work curl http://localhost/set_by_lua_demo
          1 + 2=3
          • access_by_lua指令:執(zhí)行在HTTP請求處理11個階段的access階段,使用Lua腳本進行訪問控制。運行于access階段的末尾,總是在allow和deny這樣的指令之后運行。

          格式為: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";
          }

          運行結(jié)果:

          ?  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/content_by_lua_block指令:用于設(shè)置執(zhí)行在content階段的Lua代碼塊,執(zhí)行結(jié)果將作為請求響應的內(nèi)容。該指令用于location上下文。

          格式為:content_by_lua lua-script-str

          location /errorLog {
            content_by_lua '
              ngx.log(ngx.ERR, "this is an error log ");
            	ngx.say("錯誤日志調(diào)用成功");
            ';
          }
            		
          location /infoLog {
          	content_by_lua '
          		ngx.log(ngx.ERR, "this is an info log ");
            	ngx.say("業(yè)務(wù)日志調(diào)用成功");
            ';
          }
          
          location /debugLog {
            content_by_lua '
              ngx.log(ngx.ERR, "this is an debug log ");
            	ngx.say("調(diào)試日志調(diào)用成功");
            ';
          }

          OpenResty v0.9.17版本以后,使用content_by_lua_block指令代替content_by_lua指令,避免對代碼塊中的字符串進行轉(zhuǎn)譯。

          運行結(jié)果:

          ?  work curl http://localhost/errorLog
          錯誤日志調(diào)用成功
          ?  work curl http://localhost/infoLog 
          業(yè)務(wù)日志調(diào)用成功
          ?  work curl http://localhost/debugLog
          調(diào)試日志調(diào)用成功

          Nginx Lua的內(nèi)置常量和變量

          內(nèi)置變量

          • ngx.arg:類型為Lua table,ngx.arg.VARIABLE用于獲取ngx_lua配置指令后面的調(diào)用參數(shù)。
          • ngx.var:類型為Lua table,ngx.var.VARIABLE用于引用某個Nginx變量。前提是Nginx變量必須提前聲明
          • ngx.ctx:類型為Lua table,可以用來訪問當前請求的Lua上下文數(shù)據(jù),其生存周期與當前請求相同
          • ngx.header:類型為Lua table,用于訪問HTTP響應頭,可以通過ngx.header.HEADER形式引用某個頭
          • ngx.status:用于設(shè)置當前請求的HTTP響應碼

          內(nèi)置常量

          內(nèi)置常量基本是見名知意的,可以根據(jù)后面的實戰(zhàn)案例,加深理解。

          核心常量

            • ngx.OK(0)
            • ngx.ERROR(-1)
            • ngx.AGAIN(-2)
            • ngx.DONE(-4)
            • ngx.DECLINED(-5)
            • ngx.nil

          HTTP方法常量

            • ngx.HTTP.GET
            • ngx.HTTP.HEAD
            • ngx.HTTP.PUT
            • ngx.HTTP.POST
            • ngx.HTTP.DELETE
            • ngx.HTTP.OPTIONS
            • ngx.HTTP.MKCOL
            • ngx.HTTP.MOVE
            • ngx.HTTP.PROPFIND
            • ngx.HTTP.PROPPATCH
            • ngx.HTTP.LOCK
            • ngx.HTTP.UNLOCK
            • ngx.HTTP.PATH
            • ngx.HTTP.TRACE

          HTTP狀態(tài)碼常量

            • ngx.HTTP_OK(200)
            • ngx.HTTP_CREATED(201)
            • ngx.HTTP_SPECIAL_RESPONSE(300)
            • ngx.HTTP_MOVED_PERMANENTLY(301)
            • ngx.HTTP_MOVER_TEMPORARILY(302)
            • ngx.HTTP_SEE_OTHER(303)
            • ngx.HTTP_NOT_MODIFIED(304)
            • ngx.HTTP_BAD_REQUEST(400)
            • ngx.HTTP_UNAUTHORIZED(401)
            • ngx.HTTP_FORBIDDEN(403)
            • ngx.HTTP_NOT_FOUND(404)
            • ngx.HTTP_NOT_ALLOWED(405)
            • ngx.HTTP_GONE(410)
            • ngx.HTTP_INTERNAL_SERVER_ERROR(500)

          日志類型常量

            • ngx.STDERR
            • ngx.EMERG
            • ngx.ALERT
            • ngx.CRIT
            • ngx.ERR
            • ngx.WARE
            • ngx.NOTICE
            • ngx.INFO
            • ngx.DEBUG

          Nginx+LUA基礎(chǔ)到此結(jié)束,下一篇開始實戰(zhàn)!并在實戰(zhàn)中掌握基礎(chǔ)。


          ua之參考

          Lua基礎(chǔ)語法 -Lua教程?

          Lua 教程_白玉搜一搜

          Lua注釋

          ---- 注釋
          --這是 lua 的單行注釋
          
          --[[
          這是 lua 的多行注釋
          第二行
          第三行
          --]]
          
          --[[
          這是 lua 的多行注釋
          第二行
          第三行
          ]]

          Lua交互模式(lua -i)

          > 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標識符

          Lua標識符是用于標識變量,函數(shù)或任何其他用戶定義項的名稱。 標識符以字母A到Z或a到z或下劃線_開頭,后跟零個或多個字母,下劃線和數(shù)字(0到9)。

          Lua不允許標識符中的標點符號,如@,$和%。 Lua是一種區(qū)分大小寫的編程語言。 因此,Yiibai和yiibai是Lua中的兩個不同的標識符。 以下是可接受標識符的一些示例

          mohd      zara   abc  move_name  a_123
          myname50 _temp   j    a23b9      retVal

          Lua關(guān)鍵字

          以下列表顯示了Lua中的一些保留字。 這些保留字不能用作常量或變量或任何其他標識符名稱。

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

          Lua變量

          • 全局變量 - 所有變量都被視為全局變量,除非明確聲明為局部變量。
          • 局部變量 - 當為變量指定類型為local時,其范圍受函數(shù)范圍限制。
          • 表字段 - 這是一種特殊類型的變量,可以保存除nil之外的任何內(nèi)容,包括函數(shù)。

          Lua數(shù)據(jù)類型

          編號

          值類型

          描述

          1

          nil

          用于區(qū)分值與某些數(shù)據(jù)或沒有(nil)數(shù)據(jù)。

          2

          boolean

          包括true和false作為值,通常用于條件檢查。

          3

          number

          表示實數(shù)(雙精度浮點)數(shù)字。

          4

          string

          表示字符數(shù)組。

          5

          function

          表示用C語言或Lua編寫的方法。

          6

          userdata

          表示任意C語言數(shù)據(jù)。

          7

          thread

          表示獨立的執(zhí)行線程,它用于實現(xiàn)協(xié)同程序。

          8

          table

          表示普通數(shù)組,符號表,集合,記錄,圖形,樹等,并實現(xiàn)關(guān)聯(lián)數(shù)組。 它可以保存任何值(除了nil)。

          Lua運算符

          • 算術(shù)運算符
          • 關(guān)系運算符
          • 邏輯運算符
          • 其它運算符

          算術(shù)運算符

          運算符

          描述

          示例

          +

          相加兩個操作數(shù)

          A + B=30

          -

          從第一個減去第二個操作數(shù)

          A - B=-10

          *

          將兩個操作數(shù)相乘

          A * B=200

          /

          用除分子除以分子

          B / A=2

          %

          模數(shù)運算符,整數(shù)除法后的余數(shù)

          B % A=0

          ^

          指數(shù)運算符取指數(shù)冪值

          A^2=100

          -

          一元,充當否定

          -A=-10

          關(guān)系運算符

          運算符

          描述

          示例

          ==

          檢查兩個操作數(shù)的值是否相等,如果相等,則條件變?yōu)檎妗?/p>

          (A==B)結(jié)果為false

          ~=

          檢查兩個操作數(shù)的值是否相等,如果值不相等則條件變?yōu)閠rue。

          (A ~=B)結(jié)果為true

          >

          檢查左操作數(shù)的值是否大于右操作數(shù)的值,如果是,則條件變?yōu)閠rue。

          (A > B)結(jié)果為false

          <

          檢查左操作數(shù)的值是否小于右操作數(shù)的值,如果是,則條件變?yōu)閠rue。

          (A < B)結(jié)果為true

          >=

          檢查左操作數(shù)的值是否大于或等于右操作數(shù)的值,如果是,則條件變?yōu)閠rue。

          (A >=B)結(jié)果為false

          <=

          檢查左操作數(shù)的值是否小于或等于右操作數(shù)的值,如果是,則條件變?yōu)閠rue。

          (A <=B)結(jié)果為true

          邏輯運算符

          運算符

          描述

          示例

          and

          邏輯與運算符。如果兩個操作數(shù)都不為零,則條件成立。

          (A and B) 結(jié)果為false

          or

          邏輯或運算符。 如果兩個操作數(shù)中的任何一個不為零,則條件變?yōu)檎妗?/p>

          (A or B) 結(jié)果為true

          not

          邏輯非運算符。用于反轉(zhuǎn)其操作數(shù)的邏輯狀態(tài)。 如果條件為真,則邏輯非運算符將為false。

          !(A and B)結(jié)果為true

          其它運算符

          編號

          描述

          示例

          ..

          連接兩個字符串

          如果a為Hello,b為World,a..b將返回Hello World。

          #

          返回字符串或表長度的一元運算符。

          #"Hello" 將返回 5

          Lua運算符優(yōu)先級

          具有最高優(yōu)先級的運算符顯示在表的頂部,具有最低優(yōu)先級的運算符顯示在底部。 在表達式中,將首先評估更高優(yōu)先級的運算符。

          類別

          操作符

          關(guān)聯(lián)性

          一元

          not # -

          右到左

          連接

          ..

          右到左

          乘法

          * / %

          左到右

          加法

          + -

          左到右

          關(guān)系

          < > <=>===~=

          左到右

          相等

          ==~=

          左到右

          邏輯與

          and

          左到右

          邏輯或

          or

          左到右

          Lua循環(huán)

          Lua提供以下類型的循環(huán)來處理循環(huán)需求。

          編號

          循環(huán)類型

          描述


          1

          while循環(huán)

          在給定條件為真時重復語句或語句組,它在執(zhí)行循環(huán)體之前測試條件。

          while(condition)
          do
          statement(s)
          end

          2

          for循環(huán)

          多次執(zhí)行一系列語句,并縮寫管理循環(huán)變量的代碼。

          for init,max/min value, increment
          do
          statement(s)
          end

          3

          repeat…unti循環(huán)

          重復語句組的操作,直到滿足until條件。

          repeat
          statement(s)
          until( condition )

          4

          嵌套循環(huán)

          可在任何循環(huán)中使用另一個或多個循環(huán),如:while,for或do..while循環(huán)。


          循環(huán)控制聲明

          循環(huán)控制語句從正常順序改變執(zhí)行。 當執(zhí)行離開作用域時,將銷毀在該作用域中創(chuàng)建的所有自動對象。

          Lua支持以下控制語句。

          編號

          控制語句

          描述

          1

          break語句

          終止循環(huán)并將執(zhí)行轉(zhuǎn)移到循環(huán)或switch之后的語句。

          無限循環(huán)

          如果條件永遠不會變?yōu)榧伲瑒t循環(huán)變?yōu)闊o限循環(huán)。 while循環(huán)通常用于此目的。如果直接給出了一個條件為真,它就會永遠執(zhí)行。可以使用break語句來打斷這個無限循環(huán)。

          while( true )
          do
          		print("This loop will run forever.")
          end

          Lua決策結(jié)構(gòu)

          Lua編程語言假定布爾true和non-nil值的任意組合為true,如果它是布爾false或nil,則假定為false值。 需要注意的是,在Lua中,零將被視為true。

          Lua編程語言提供以下類型的決策制定語句 -

          編號

          決策語句

          描述

          1

          if語句

          if語句由布爾表達式后跟一個或多個語句組成。

          2

          if…else語句

          if語句后面可以跟一個可選的else語句,該語句在布爾表達式為false時執(zhí)行。

          3

          嵌套if語句

          在一個if或else if語句中使用另一個if或else if語句。

          Lua函數(shù)

          Lua編程語言中方法定義的一般形式如下 -

          optional_function_scope function function_name( argument1, argument2, argument3........, argumentn)
              function_body
          return result_params_comma_separated
          end

          Lua編程語言中的方法定義由方法頭和方法體組成。以下是方法的所有部分 -

          • 可選函數(shù)范圍 - 使用關(guān)鍵字local來限制函數(shù)的范圍,或者忽略scope部分,這會變成一個全局函數(shù)。
          • 函數(shù)名稱 - 這是函數(shù)的實際名稱。 函數(shù)名稱和參數(shù)列表一起構(gòu)成函數(shù)簽名。
          • 參數(shù) - 參數(shù)就像一個占位符。 調(diào)用函數(shù)時,將值傳遞給參數(shù)。 該值稱為實際參數(shù)或參數(shù)。 參數(shù)列表指的是方法的參數(shù)的類型,順序和數(shù)量。 參數(shù)是可選的; 也就是說,方法的參數(shù)可有可無。
          • 函數(shù)主體 - 方法體包含一組語句,用于定義方法的作用。
          • 返回 - 在Lua中,可通過使用return關(guān)鍵字,后加逗號分隔返回值列表來返回多個值。

          Lua字符串

          字符串是由一系列字符以及控制字符組成,如換頁符。 字符串可以用三種形式初始化,包括 -

          • 單引號之間的字符
          • 雙引號之間的字符
          • [[和]]之間的字符
          string1="Lua"
          print("\"String 1 is\"",string1)
          
          string2='Yiibai Tutorial'
          print("String 2 is",string2)
          
          string3=[["Lua Tutorial"]]
          print("String 3 is",string3)

          字符串中使用轉(zhuǎn)義序列字符來更改字符的正常解釋。例如,要打印雙引號(""),在上面的示例中使用了"。轉(zhuǎn)義序列及其用法列在下表中。

          轉(zhuǎn)義序列

          用法

          \a

          \b

          退格

          \f

          換頁

          \n

          新行

          \r

          回車

          \t

          制表符

          \v

          垂直制表符

          \

          反斜杠

          "

          雙引號

          '

          單引號

          \[

          左方括號

          \]

          右方括號

          字符串操作

          Lua支持字符串來操作字符串 -

          編號

          方法

          作用

          1

          string.upper(argument)

          返回參數(shù)的大寫表示。

          2

          string.lower(argument)

          返回參數(shù)的小寫表示。

          3

          string.gsub(mainString,findString,replaceString)

          返回用replaceString替換findString后字符串。

          4

          string.find(mainString,findString, optionalStartIndex,optionalEndIndex)

          返回mainString中findString的起始索引和結(jié)束索引找到字符串,如果未找到則返回nil。

          5

          string.reverse(arg)

          將傳遞的字符串a(chǎn)rg反轉(zhuǎn)后的字符串。

          6

          string.format(...)

          返回格式化字符串。

          7

          string.char(arg) 和 string.byte(arg)

          返回輸入?yún)?shù)的內(nèi)部數(shù)字和字符表示。

          8

          string.len(arg)

          返回傳遞字符串a(chǎn)rg的長度。

          9

          string.rep(string, n))

          通過重復相同的字符串n次返回一個字符串。

          10

          ..

          此運算符連接兩個字符串。

          Lua數(shù)組

          數(shù)組是對象的有序排列,可以是包含行集合的一維數(shù)組或包含多個行和列的多維數(shù)組。

          在Lua中,使用帶整數(shù)的索引表實現(xiàn)數(shù)組。 數(shù)組的大小不固定,它可以根據(jù)要求增長,受內(nèi)存限制。

          一維數(shù)組

          array={"Lua", "Tutorial"}
          for i=0, 2 do
              print(array[i])
          end

          多維數(shù)組

          -- 初始化數(shù)組
          array={}
          for i=1,3 do
            array[i]={}
            for j=1,3 do
              array[i][j]=i*j
            end
          end
          
          -- 訪問數(shù)組
          for i=1,3 do
            for j=1,3 do
              print(array[i][j])
            end
          end

          Lua迭代器

          迭代器泛型

          迭代器泛型提供集合中每個元素的鍵值對。 下面給出一個簡單的例子。

          array={"Lua", "Tutorial"}
          for key,value in ipairs(array) 
          do
            print(key, value)
          end

          上面的例子使用了Lua提供的默認ipairs迭代器函數(shù)。

          在Lua中,使用函數(shù)來表示迭代器。 基于這些迭代器函數(shù)中的狀態(tài)維護,有兩種主要類型 -

          • 無狀態(tài)迭代器
          • 有狀態(tài)迭代器

          無狀態(tài)迭代器

          通過名稱,可以理解這種類型的迭代器函數(shù)不會保留任何狀態(tài)。下面來看一個使用打印n個數(shù)字的函數(shù)square創(chuàng)建迭代器的示例。

          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函數(shù)的工作方式。 如下代碼所示 -

          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

          有狀態(tài)迭代器

          上面使用函數(shù)的迭代示例不保留狀態(tài)。 每次調(diào)用該函數(shù)時,它都會根據(jù)發(fā)送給函數(shù)的第二個變量返回集合的下一個元素。 要保持當前元素的狀態(tài),可使用閉包(Closure)。 閉包在函數(shù)調(diào)用中保留變量值。 要創(chuàng)建一個新的閉包,這里首先創(chuàng)建兩個函數(shù),包括閉包本身和一個工廠,即創(chuàng)建閉包的函數(shù)。

          現(xiàn)在來看看一個創(chuàng)建迭代器的例子,并將使用閉包。

          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來通過每次調(diào)用函數(shù)時遞增索引來返回集合中的每個元素。 可使用閉包創(chuàng)建函數(shù)迭代器,如上所示,它可以為迭代整個集合的每個時間返回多個元素。

          Lua表

          表是Lua中唯一可用的數(shù)據(jù)結(jié)構(gòu),使用表可以創(chuàng)建不同的類型,如數(shù)組和字典。 Lua使用關(guān)聯(lián)數(shù)組,不僅可使用數(shù)字編制索引,還可以使用除nil之外的字符串編制索引。 表沒有固定的大小,可以根據(jù)需要增長大小。

          Lua在所有表示中使用表,包括包的表示。 當訪問方法string.format時,這意味著,訪問字符串包中可用的格式函數(shù)。

          表示和用法

          表稱為對象,它既不是值也不是變量。 Lua使用構(gòu)造函數(shù)表達式{}來創(chuàng)建一個空表。 應該知道,保持表的引用的變量和表本身之間沒有固定的關(guān)系。

          --sample table initialization
          mytable={}
          
          --simple table value assignment
          mytable[1]="Lua"
          
          --removing reference
          mytable=nil
          
          -- lua garbage collection will take care of releasing memory

          假設(shè)有一個包含元素集的表a,如果將它分配給表b,則a和b都指向相同的內(nèi)存。 Lua不會單獨為b分配單獨的內(nèi)存。 當a設(shè)置為nil時,b仍然可以訪問表。 當沒有對表的引用時,Lua中的垃圾收集器負責清理進程以使這些未引用的內(nèi)存再次被重用。

          下面示出了一個例子,用于解釋表的上述特征。

          -- 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

          表操作

          下面是用于表操作的內(nèi)置函數(shù),它們列在下表格中。

          編號

          方法

          作用

          1

          table.concat(table [, sep [, i [, j]]])

          根據(jù)給定的參數(shù)連接表中的字符串。詳細信息請參見示例。

          2

          table.insert(table, [pos,] value)

          在指定位置的表中插入值。

          3

          table.maxn(table)

          返回最大的數(shù)字索引。

          4

          table.remove(table [, pos])

          從表中刪除值。

          5

          table.sort(table [, comp])

          根據(jù)可選的比較器參數(shù)對表進行排序。

          下面來看看一些上述功能的示例。

          1. 表連接

          使用concat函數(shù)來連接兩個表,如下所示 -

          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

          2. 插入和刪除

          在表操作中,最常見的是在表中插入和刪除項目。如下解釋。

          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

          3. 排序表

          有時想要按特定順序?qū)Ρ磉M行排序。 排序函數(shù)按字母順序?qū)Ρ碇械脑剡M行排序。 這方面的示例如下所示。

          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

          Lua模塊

          模塊是一個可以使用require加載的庫,并且只有一個包含表的全局名稱。 模塊可以包含許多功能和變量。 所有這些函數(shù)和變量都包含在表中,表充當命名空間。 此外,一個良好的模塊有必要的子句,以在使用require語句時返回此表。

          Lua模塊的特色

          模塊中表的使用以多種方式,能夠使用與操作任何其他Lua表相同的方式操作模塊。 由于能夠操作模塊,它提供了其他語言需要特殊機制的額外功能。 由于Lua中模塊的這種自由機制,用戶可以通過多種方式調(diào)用Lua中的函數(shù)。 其中一些操作示例如下所示。

          -- 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中的編程靈活性,沒有任何特殊的附加代碼。

          require函數(shù)

          Lua提供了一個名為require的高級函數(shù)來加載所有必需的模塊。 它保持盡可能簡單,以避免有太多關(guān)于模塊的信息來加載。 require函數(shù)只是將模塊假定為一塊代碼,它定義了一些值,實際上是包含函數(shù)或表。

          示例

          考慮一個簡單的例子,其中一個函數(shù)是數(shù)學函數(shù)。 將此模塊稱為mymath,文件名為mymath.lua。 文件的代碼內(nèi)容如下 -

          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

          現(xiàn)在,為了在另一個文件(例如,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)中,它需要額外的設(shè)置。 當運行上面的程序時,將得到以下輸出 -

          30
          10
          200
          1.5

          注意事項

          • 將運行的模塊和文件放在同一目錄中。
          • 模塊名稱及其文件名應相同。
          • 使用require函數(shù)返回模塊,因此模塊最好如上所示實現(xiàn),盡管可以在其他地方找到其他類型的實現(xiàn)。

          實現(xiàn)模塊的舊方法

          下面將以舊方式重寫上面相同的示例,它使用package.seeall類型的實現(xiàn)。 這在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)

          當運行上面的操作時,將獲得相同的輸出。 但建議使用較舊版本的代碼,并假設(shè)它不太安全。 許多使用Lua進行編程的SDK如Corona SDK都不推薦使用它。

          Lua元表

          元表(metatable)是一個表,它是使用鍵集和相關(guān)元方法來修改附加到的表的行為。 這些元方法是強大的Lua功能,可實現(xiàn)如下功能 -

          • 在表上更改/添加功能到操作符。
          • 使用元表中的__index在表中沒有鍵時查找元表。

          在處理元表時有兩種重要的方法,包括 -

          • setmetatable(table,metatable) - 此方法用于為表設(shè)置元表。
          • getmetatable(table) - 此方法用于獲取表的元表。

          首先來看看如何將一個表設(shè)置為另一個表的元表。 如下所示 -

          mytable={}
          mymetatable={}
          setmetatable(mytable,mymetatable)
          getmetatable(mytable)

          上面的代碼可以用一行表示,如下所示 -

          mytable=setmetatable({},{})
          getmetatable(mytable)

          部分元方法介紹

          __index     -- 'table[key]',取下標操作,用于訪問表中的域
          __newindex  -- 'table[key]=value',賦值操作,增改表中的域
          __call      -- 'func(args)',函數(shù)調(diào)用,(參見 《Lua 學習筆記(三)—— 表達式》中的函數(shù)部分介紹)
          
          -- 數(shù)學運算操作符
          __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

          __index

          下表顯示了元表在表中不可用時查找元表的示例。

          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)

          當運行上面的程序時,將得到以下輸出結(jié)果 -

          value1 metatablevalue

          下面來逐步看看上面例子中發(fā)生的事情。

          • 這里表mytable是{key1="value1"}。
          • 元表設(shè)置為mytable,其中包含__index的函數(shù),它稱為元方法。
          • 元方法執(zhí)行查找索引key2,如果找到它,則返回metatablevalue,否則返回相應索引的mytable值。

          上述程序的簡化版本,如下所示 -

          mytable=setmetatable({key1="value1"},{ __index={ key2="metatablevalue" } })
          print(mytable.key1,mytable.key2)

          __newindex

          當將__newindex添加到metatable時,如果表中沒有鍵,則新鍵的行為將由元方法定義。 下面給出了當主表中沒有索引時設(shè)置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函數(shù)更新同一個表的另一個示例如下所示 -

          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設(shè)置值而不使用元表的__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

          使用__call語句添加方法調(diào)用的行為。 一個簡單的示例,它返回主表中的值與傳遞的表的總和。

          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

          __tostring

          要更改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

          如果完全了解元表的功能,那么可以真正執(zhí)行很多非常復雜的操作。 因此,嘗試使用元表中可用的不同選項的元表更多地工作。

          Lua協(xié)同程序 ??

          協(xié)同程序本質(zhì)上是協(xié)作的,它允許兩種或多種方法以受控方式執(zhí)行。 使用協(xié)同程序,在任何給定時間,只有一個協(xié)同程序運行,并且此運行協(xié)程僅在顯式請求暫停時暫停執(zhí)行。

          上述定義可能看起來含糊不清。 假設(shè)有兩種方法,
          一種是主程序方法,另一種是協(xié)程。 當使用resume函數(shù)調(diào)用一個協(xié)程時,它會開始執(zhí)行,當調(diào)用yield函數(shù)時,它會暫停執(zhí)行。
          同樣的協(xié)同程序可以繼續(xù)執(zhí)行另一個恢復函數(shù)調(diào)用,協(xié)同程序就會暫停。 該過程可以持續(xù)到協(xié)程執(zhí)行結(jié)束。

          協(xié)同程序函數(shù)

          下表列出了Lua中協(xié)程的所有可用函數(shù)及其相應的用法。

          編號

          方法

          作用或目的

          1

          coroutine.create (f)

          使用函數(shù)f創(chuàng)建一個新的協(xié)同程序,并返回thread類型的對象。

          2

          coroutine.resume (co [, val1, ...])

          恢復協(xié)程co并傳遞參數(shù)(如果有的話)。它返回操作狀態(tài)和可選的其他返回值。

          3

          coroutine.running ()

          如果在主線程中調(diào)用,則返回正在運行的協(xié)同程序或nil。

          4

          coroutine.status (co)

          根據(jù)協(xié)同程序的狀態(tài)返回running,normal,suspended或dead中的一個值。

          5

          coroutine.wrap (f)

          與coroutine.create一樣,coroutine.wrap函數(shù)也會創(chuàng)建一個協(xié)同程序,但它不會返回協(xié)同程序本身,而是返回一個函數(shù),當調(diào)用它時,它會恢復協(xié)同程序。

          6

          coroutine.yield (...)

          暫停正在運行的協(xié)同程序。 傳遞給此方法的參數(shù)充當resume函數(shù)的附加返回值。

          示例 下面來看一個例子,通過此示例來理解協(xié)同程序的概念。

          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

          上面的例子是實現(xiàn)什么功能?

          如前所述,使用resume函數(shù)來啟動操作和yield函數(shù)來停止操作。 此外,可以看到coroutine的恢復功能接收到多個返回值。

          • 首先,創(chuàng)建一個協(xié)同程序并分配給變量名稱co,協(xié)同程序?qū)蓚€變量作為參數(shù)。
          • 當調(diào)用第一個恢復函數(shù)時,值3和2保留在臨時變量value1和value2中,直到協(xié)程結(jié)束。
          • 使用了一個變量tempvar3,它最初值是10,并且通過后續(xù)的協(xié)程調(diào)用更新為13和16,在整個協(xié)程的執(zhí)行過程中value1的值保持為3。
          • 第一個coroutine.yield將兩個值4和3返回到resume函數(shù),通過更新yield語句中的輸入?yún)?shù)為3和2。 它還接收協(xié)程執(zhí)行的true/false狀態(tài)。
          • 關(guān)于協(xié)同程序的另一個問題是,在上面的例子中如何處理下一個resume調(diào)用的句子; 可以看到變量coroutine.yield接收下一個調(diào)用參數(shù),它提供了一種強大的方法,可以通過保留現(xiàn)有的參數(shù)值來進行新的操作。
          • 最后,當協(xié)程中的所有語句都執(zhí)行后,后續(xù)調(diào)用將返回false并且響應語句為:cannot resume dead coroutine

          另一個協(xié)同程序示例

          下面來看一個簡單的協(xié)同程序示例,它使用yield函數(shù)和resume函數(shù)返回1到5之間的數(shù)字。 如果不可用它會創(chuàng)建協(xié)程,或者恢復現(xiàn)有的協(xié)程。

          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

          通常會將協(xié)同程序與多路程序設(shè)計語言的線程進行比較,但需要了解協(xié)同程序具有類似的線程功能,但協(xié)同程序一次只執(zhí)行一個程序,并且永遠不會同時執(zhí)行。

          通過暫時保留某些信息來控制程序執(zhí)行順序以滿足需求。 使用帶有協(xié)同程序的全局變量為協(xié)同程序提供了更大的靈活性。

          Lua文件操作


          I/O庫用于讀取和操作Lua中的文件。 Lua中有兩種文件操作,即隱式文件描述符和顯式文件描述符。

          對于以下示例,將使用示例文件 - test.lua,內(nèi)容如下所示 -

          -- sample test.lua
          -- sample2 test.lua

          文件打開操作使用以下語句 -

          file=io.open (filename [, mode])

          下表列出了各種文件模式 -

          編號

          模式

          描述

          1

          r

          只讀模式,是打開現(xiàn)有文件的默認模式。

          2

          w

          寫入啟用模式,覆蓋現(xiàn)有文件或創(chuàng)建新文件。

          3

          a

          附加模式,用于打開現(xiàn)有文件或創(chuàng)建新文件以進行追加。

          4

          r+

          現(xiàn)有文件的讀寫模式。

          5

          w+

          如果文件存在或創(chuàng)建具有讀寫權(quán)限的新文件,則刪除所有現(xiàn)有數(shù)據(jù)。

          6

          a+

          啟用了讀取模式的追加模式可打開現(xiàn)有文件或創(chuàng)建新文件。

          1. 隱式文件描述符

          隱式文件描述符使用標準輸入/輸出模式或使用單個輸入和單個輸出文件。 使用隱式文件描述符的示例如下所示。

          -- 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 文件第一行的輸出。執(zhí)行上面程序,得到以下輸出 -

          -- Sample test.lua

          這是test.lua 文件中聲明的第一行。 同時也將"-- End of the test.lua file"這一行附加到test.lua 文件中的最后一行。

          在上面的示例中,可以使用io."x"方法查看隱式描述符如何與文件系統(tǒng)一起使用。 上面的示例使用不帶可選參數(shù)的io.read()方法。可選參數(shù)可以是以面中的任何一個。

          編號

          模式

          描述

          1

          *n

          從當前文件位置讀取并返回一個數(shù)字(如果存在于文件位置或返回nil)。

          2

          *a

          從當前文件位置返回文件的所有內(nèi)容。

          3

          *l

          從當前文件位置讀取行,并將文件位置移動到下一行。

          4

          number

          讀取函數(shù)中指定的字節(jié)數(shù)。

          其他常見的I/O方法包括,

          • io.tmpfile() - 返回一個臨時文件,用于讀取和寫入,當程序退出,將刪除該文件。
          • io.type(file) - 根據(jù)輸入文件返回文件,關(guān)閉文件或nil。
          • io.flush() - 清除默認輸出緩沖區(qū)。
          • io.lines(可選文件名) - 提供循環(huán)迭代器的通用循環(huán)迭代器,循環(huán)遍歷文件并最終關(guān)閉文件,以防提供文件名或使用默認文件而不在循環(huán)結(jié)束時關(guān)閉。

          2. 顯式文件描述符

          顯式文件描述符經(jīng)常使用,它允許一次操作多個文件。 這些函數(shù)與隱式文件描述符非常相似。 在這里,使用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

          文件打開的所有模式和用于讀取外部描述符的參數(shù)與隱式文件描述符相同。

          其他常見的文件方法包括,

          • file:seek(optional whence, optional offset) - 參數(shù)是set,cur或end。 使用文件開頭的更新文件位置設(shè)置新文件指針。 在此函數(shù)中,偏移量為零。 如果第一個參數(shù)是set,則從文件的開頭測量偏移量; 從文件中的當前位置開始,如果它是cur; 或者從文件的末尾開始,則它是end。 默認參數(shù)值為cur和0,因此可以通過不帶參數(shù)調(diào)用此函數(shù)來獲取當前文件位置。
          • file:flush() ? 清除默認輸出緩沖區(qū)。
          • io.lines(optional file name) - 提供循環(huán)迭代器的通用循環(huán)迭代器,循環(huán)遍歷文件并最終關(guān)閉文件,以防提供文件名或使用默認文件而不在循環(huán)結(jié)束時關(guān)閉。

          使用搜索方法的示例如下所示。它將光標從文件結(jié)束前的25個位置偏移。 read函數(shù)從搜索位置打印文件的剩余部分。

          -- 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()

          執(zhí)行上面示例代碼,將獲得類似于以下內(nèi)容的輸出 -

          sample2 test.lua
          --test

          讀者可自己使用所有的模式和參數(shù)來了解Lua文件操作的完整功能。

          Lua錯誤處理

          錯誤處理非常關(guān)鍵,因為實際操作通常需要使用復雜的操作,包括文件操作,數(shù)據(jù)庫事務(wù)和Web服務(wù)調(diào)用。

          在任何編程中,總是需要錯誤處理。 錯誤可以是兩種類型,它們包括 -

          • 語法錯誤
          • 運行時錯誤

          1. 語法錯誤

          由于不正確地使用各種程序組件(如運算符和表達式),從而發(fā)生語法錯誤。 語法錯誤的一個簡單示例如下所示-

          a==2

          使用單個“單等于號”和“雙等于號”之間存在差異。 使用不當可能導致錯誤。 一個“等于”指的是賦值,而兩個“等于”指的是比較。

          語法錯誤的另一個示例如下所示 -

          for a=1,10
            print(a)
          end

          當運行上述程序時,將獲得以下輸出 -

          lua: test2.lua:2: 'do' expected near 'print'

          語法錯誤比運行時錯誤更容易處理,因為Lua解釋器比運行時錯誤更清楚地定位錯誤。 從上面的錯誤中,可以很容易地知道根據(jù)Lua結(jié)構(gòu)在print語句之前添加do語句。

          2. 運行時錯誤

          如果出現(xiàn)運行時錯誤,程序?qū)⒊晒?zhí)行,但由于輸入錯誤或錯誤處理的函數(shù),可能會導致運行時錯誤。 顯示運行時錯誤的簡單示例如下所示。

          function add(a,b)
          		return a+b
          end
          
          add(10)

          當構(gòu)建上面程序時,它將成功構(gòu)建并運行。 運行后,顯示運行時錯誤。

          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]: ?

          這是由于未傳遞兩個參數(shù)變量而發(fā)生的運行時錯誤。 b參數(shù)是預期的,但是因為它未傳入,默認使用的是nil從而產(chǎn)生錯誤。

          斷言和錯誤功能

          要處理錯誤,經(jīng)常使用兩個函數(shù)是 - 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)

          當構(gòu)建上面程序時,它將成功構(gòu)建并運行。 運行后,顯示運行時錯誤。

          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])終止最后一個調(diào)用的受保護函數(shù),并將消息作為錯誤消息返回。 此函數(shù)錯誤永遠不會返回。 通常,錯誤會在消息開頭添加有關(guān)錯誤位置的一些信息。 level參數(shù)指定如何獲取錯誤位置。 對于級別1(默認值),錯誤位置是調(diào)用錯誤函數(shù)的位置。 級別2將錯誤指向調(diào)用調(diào)用錯誤的函數(shù)的位置等等。 傳遞0級可避免向消息添加錯誤位置信息。

          pcall和xpcall

          在Lua編程中,為了避免拋出這些錯誤和處理錯誤,需要使用pcall或xpcall函數(shù)。

          pcall(f,arg1,...)函數(shù)在保護模式下調(diào)用所請求的函數(shù)。 如果函數(shù)f中發(fā)生某些錯誤,則不會拋出錯誤。 它只返回錯誤狀態(tài)。 使用pcall的一個簡單示例如下所示。

          function myfunction()
            n=n/nil
          end
          
          if pcall(myfunction) then
            print("Success")
          else
            print("Failure")
          end

          當構(gòu)建上面程序時,它將成功構(gòu)建并運行。 運行后,顯示運行時錯誤。

          Failure

          xpcall(f,err)函數(shù)調(diào)用所請求的函數(shù),并設(shè)置錯誤處理程序。 f內(nèi)的任何錯誤都不會傳播; 而xpcall只捕獲錯誤,使用原始錯誤對象調(diào)用err函數(shù),并返回狀態(tài)代碼。

          xpcall函數(shù)的一個簡單示例如下所示 -

          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調(diào)試 ??

          Lua提供了一個調(diào)試庫,它提供了創(chuàng)建自己的調(diào)試器的原始函數(shù)。 即使沒有內(nèi)置的Lua調(diào)試器,也有許多第三方Lua調(diào)試器,由各種開發(fā)人員創(chuàng)建,其中許多是開源的。

          下表中列出了Lua調(diào)試庫中可用的功能及其用法。

          編號

          方法

          描述

          1

          debug()

          進入用于調(diào)試的交互模式,該模式保持活動狀態(tài),直到用戶只輸入一行中的cont并按Enter鍵。 用戶可以使用其他功能在此模式下檢查變量。

          2

          getfenv(object)

          返回對象的環(huán)境。

          3

          gethook(optional thread)

          返回線程的當前掛鉤設(shè)置,有三個值 - 當前掛鉤函數(shù),當前掛鉤掩碼和當前掛鉤計數(shù)。

          4

          getinfo(optional thread, function or stack level, optional flag)

          返回一個包含函數(shù)信息的表。可以直接給出函數(shù),或者可以給一個數(shù)字作為函數(shù)的值,在給定線程的調(diào)用堆棧的級別函數(shù)上運行的函數(shù) - 級別0為當前函數(shù)(getinfo本身); 級別1為調(diào)用getinfo的函數(shù);等等。 如果function是一個大于活動函數(shù)數(shù)的數(shù)字,則getinfo返回nil。

          5

          getlocal(optional thread, stack level, local index)


          6

          getmetatable(value)

          返回給定對象的元表,如果沒有元表,則返回nil。

          7

          getregistry()

          返回注冊表表,這是一個預定義的表,任何C語言代碼都可以使用它來存儲它需要存儲的任何Lua值。

          8

          getupvalue(function, upvalue index)

          此函數(shù)返回upvalue的名稱和值,索引為函數(shù)func。 如果給定索引沒有upvalue,則函數(shù)返回nil。

          9

          setfenv(function or thread or userdata, environment table)

          將給定對象的環(huán)境設(shè)置為給定表,返回對象。

          10

          sethook(optional thread, hook function, hook mask string with "c" and/or "r" and/or "l", optional instruction count)

          將給定函數(shù)設(shè)置為鉤子。 字符串掩碼和數(shù)字計數(shù)描述了何時調(diào)用掛鉤。 這里,每次Lua調(diào)用,返回并分別輸入函數(shù)中的每一行代碼時,都會調(diào)用c,r和l。

          11

          setlocal(optional thread, stack level, local index, value)

          使用堆棧級別的函數(shù)的索引local將值賦給局部變量。 如果沒有具有給定索引的局部變量,則該函數(shù)返回nil,并且當使用超出范圍的級別調(diào)用時引發(fā)錯誤。 否則,它返回局部變量的名稱。

          12

          setmetatable(value, metatable)

          將給定對象的metatable設(shè)置為給定表(可以為nil)。

          13

          setupvalue(function, upvalue index, value)

          此函數(shù)使用函數(shù)func的索引up將值賦給upvalue。 如果給定索引沒有upvalue,則函數(shù)返回nil。 否則,它返回upvalue的名稱。

          14

          traceback(optional thread, optional message string, optional level argument)

          使用回溯構(gòu)建擴展錯誤消息。

          上面的列表是Lua中完整的調(diào)試函數(shù)列表,經(jīng)常使用一個上述函數(shù)的庫,并提供更簡單的調(diào)試。 使用這些函數(shù)并創(chuàng)建我們自己的調(diào)試器非常復雜,并不是優(yōu)選的。 下面來看一個簡單使用調(diào)試功能的例子。

          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

          在上面的示例程序中,使用調(diào)試庫中提供的debug.trace函數(shù)打印堆棧跟蹤。 debug.getinfo獲取函數(shù)的當前表。

          調(diào)試示例

          開發(fā)人員經(jīng)常為了知道函數(shù)的局部變量以進行調(diào)試。 為此,可以使用getupvalue和使用setupvalue并設(shè)置這些局部變量。 一個簡單的例子如下所示 -

          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

          在此示例中,計數(shù)器每次調(diào)用時都會更新一次。 可以使用getupvalue函數(shù)查看局部變量的當前狀態(tài)。 然后,將局部變量設(shè)置為新值。 這里,在調(diào)用set操作之前n為2。 使用setupvalue函數(shù),它更新為10。現(xiàn)在當調(diào)用計數(shù)器函數(shù)時,它將返回11而不是3。

          調(diào)試類型

          在Lua中,調(diào)試類型主要有兩種 -

          • 命令行調(diào)試
          • 圖形化調(diào)試

          1. 命令行調(diào)試

          命令行調(diào)試是使用命令和打印語句在命令行進行調(diào)試的調(diào)試類型。 Lua有許多命令行調(diào)試器,下面列出了一些。

          • RemDebug - RemDebug是Lua 5.0和Lua 5.1的遠程調(diào)試器。 它允許遠程控制另一個Lua程序的執(zhí)行,設(shè)置斷點并檢查程序的當前狀態(tài)。 RemDebug 還可以調(diào)試CGILua腳本。
          • clidebugger - 它是Lua 5.1的簡單命令行界面調(diào)試器,用純Lua編寫。 除了標準的Lua 5.1庫之外,它不依賴于任何其他東西。 它的靈感來自RemDebug,但沒有遠程工具。
          • ctrace - 用于跟蹤Lua API調(diào)用的工具。
          • xdbLua - 用于Windows平臺的簡單Lua命令行調(diào)試程序。
          • LuaInterface Debugger - 它是LuaInterface的調(diào)試器擴展。 它將內(nèi)置的Lua調(diào)試接口提升到更高的級別。與調(diào)試器的交互由事件和方法調(diào)用完成。
          • Rldb - 它是一個通過套接字的遠程Lua調(diào)試器,可在Windows和Linux上使用。 它可以為您提供比現(xiàn)有功能更多的功能。
          • ModDebug - 這允許遠程控制另一個Lua程序的執(zhí)行,設(shè)置斷點,并檢查程序的當前狀態(tài)。

          2. 圖形調(diào)試

          在IDE下可以進行圖形調(diào)試,在IDE中可為各種狀態(tài)(如變量值,堆棧跟蹤和其他相關(guān)信息)進行可視化調(diào)試。 借助斷點,步入,步進和IDE中的其他按鈕,可以實現(xiàn)可視化表示和逐步控制執(zhí)行。

          Lua有許多圖形調(diào)試器,它包括以下內(nèi)容-

          • SciTE - Lua的默認Windows IDE提供了多個調(diào)試工具,如斷點,步驟,步入,步進,監(jiān)視變量等。
          • Decoda - 這是一個具有遠程調(diào)試支持的圖形調(diào)試器。
          • ZeroBrane Studio - Lua IDE集成了遠程調(diào)試器,堆棧視圖,監(jiān)視視圖,遠程控制臺,靜態(tài)分析器等。 適用于LuaJIT,Love2d,Moai和其他Lua引擎; Windows,OSX和Linux。
          • akdebugger - 這是Eclipse調(diào)試器和編輯器的Lua插件。
          • luaedit - 這包括遠程調(diào)試,本地調(diào)試,語法突出顯示,完成建議列表,參數(shù)命題引擎,高級斷點管理(包括斷點和命中計數(shù)的條件系統(tǒng)),功能列表,全局和局部變量列表,監(jiān)視,面向解決方案的管理。

          Lua垃圾收集

          Lua使用自動內(nèi)存管理,該管理使用基于Lua內(nèi)置的某些算法的垃圾收集。自動內(nèi)存管理收集,作為開發(fā)人員 -

          • 無需擔心為對象分配內(nèi)存。
          • 除非將其設(shè)置為零,否則無需在不再需要時釋放它們。

          Lua使用垃圾收集器它在后臺會時不時地運行,收集和銷毀Lua程序訪問的死對象。

          包括表,用戶數(shù)據(jù),函數(shù),線程,字符串等在內(nèi)的所有對象都需要進行自動內(nèi)存管理。 Lua使用增量標記和掃描收集器,它使用兩個數(shù)字來控制其垃圾收集周期,即垃圾收集器暫停和垃圾收集器步驟倍增器。 這些值以百分比表示,值100通常在內(nèi)部等于1。

          垃圾收集器暫停

          垃圾收集器暫停用于控制垃圾收集器之前需要等待多長時間; Lua的自動內(nèi)存管理再次調(diào)用。 小于100的值意味著Lua不會等待下一個周期。 類似地,該值的較高值將導致垃圾收集器緩慢。 200的值表示收集器在開始新循環(huán)之前等待使用的總內(nèi)存加倍。 因此,根據(jù)應用程序的性質(zhì)和速度,可能需要更改此值以在Lua應用程序中獲得最佳性能。

          垃圾收集器步驟乘數(shù)

          此步驟乘數(shù)控制垃圾收集器與Lua程序中內(nèi)存分配的相對速度。 較大的步長值將導致垃圾收集器更具侵略性,并且還會增加垃圾收集的每個增量步驟的步長。 小于100的值通常可以避免垃圾收集器不完成循環(huán)并且通常不是優(yōu)選的。 默認值為200,這意味著垃圾收集器的運行速度是內(nèi)存分配的兩倍。

          垃圾收集器函數(shù)

          作為開發(fā)人員,確實可以控制Lua中的自動內(nèi)存管理。有以下方法可以使用。

          • collectgarbage("collect") -運行一個完整的垃圾收集循環(huán)。
          • collectgarbage("count") - 返回程序當前以千字節(jié)為單位的內(nèi)存量。
          • collectgarbage("restart") - 如果垃圾收集器已停止,則重新啟動它。
          • collectgarbage("setpause") - 將給定的值作為第二個參數(shù)除以100,將它設(shè)置為垃圾收集器暫停變量。
          • collectgarbage("setstepmul") - 將給定的值作為第二個參數(shù)除以100,將它設(shè)置為垃圾步驟乘數(shù)變量。
          • collectgarbage("step") - 運行垃圾收集的一步。第二個參數(shù)越大,此步驟越大。 如果觸發(fā)的步驟是垃圾收集周期的最后一步,則collectgarbage將返回true。
          • collectgarbage("stop") - 如果垃圾收集器正在運行,則停止它。

          使用垃圾收集器示例如下所示。

          mytable={"apple", "orange", "banana"}
          print(collectgarbage("count"))
          mytable=nil
          print(collectgarbage("count"))
          print(collectgarbage("collect"))
          print(collectgarbage("count"))

          當運行上面的程序時,將得到以下輸出。 請注意,由于操作系統(tǒng)類型的不同以及Lua的自動內(nèi)存管理功能,此結(jié)果會有所不同。

          22.599609375
          20.79296875
          0
          21.5537109375

          在上面的程序中看到,完成垃圾收集,它就減少了使用的內(nèi)存。 但是,這并不是強制要求。 即使不調(diào)用它們,它也會在預定義的時間段后由Lua解釋器在稍后階段自動執(zhí)行。

          顯然,如果需要,可以使用這些函數(shù)來改變垃圾收集器的行為。 這些函數(shù)為開發(fā)人員提供了一些額外的功能來處理復雜的情況。 根據(jù)程序所需的內(nèi)存類型,也可能不會使用此功能。 但了解應用程序中的內(nèi)存使用情況并在編程期間檢查它以避免在部署后出現(xiàn)意外結(jié)果非常有用。

          Lua面向?qū)ο缶幊?/h1>

          面向?qū)ο缶幊?OOP)是現(xiàn)代編程中使用最多的編程技術(shù)之一。 有許多支持OOP的編程語言包括 -

          • C++
          • Java
          • Objective-C
          • Smalltalk
          • C#
          • Ruby

          面向?qū)ο蟮奶攸c

          • - 類是用于創(chuàng)建對象的可擴展模板,為狀態(tài)(成員變量)和行為實現(xiàn)提供初始值。
          • 對象 - 它是類的一個實例,并為自己分配了單獨的內(nèi)存。
          • 繼承 - 這是一個概念,通過該概念,一個類的變量和函數(shù)由另一個類繼承。
          • 封裝 - 這是在類中組合數(shù)據(jù)和函數(shù)的過程。借助函數(shù)可以在類的外部訪問數(shù)據(jù)。 它也被稱為數(shù)據(jù)抽象。

          Lua面向?qū)ο?/h1>

          可使用Lua的表和第一類函數(shù)在Lua中實現(xiàn)面向?qū)ο蟆?通過將函數(shù)和相關(guān)數(shù)據(jù)放入表中,形成對象。可使用元表實現(xiàn)繼承,為父對象中不存在的函數(shù)(方法)和字段提供查找機制。

          Lua中的表具有狀態(tài)和標識等對象的特征,與其值無關(guān)。 具有相同值的兩個對象(表)是不同的對象,而對象在不同的時間可以具有不同的值,但它始終是相同的對象。 與對象一樣,表的生命周期與誰創(chuàng)建它們或創(chuàng)建位置無關(guān)。

          真實世界的例子

          面向?qū)ο蟮母拍顝V泛使用,但需要清楚地理解面向?qū)ο笠垣@得適當和最大的好處。 考慮一個簡單的數(shù)學例子。 我們經(jīng)常遇到處理不同形狀的情況,如圓形,矩形和方形。

          形狀可以具有共同的屬性area。 因此,使用公共屬性區(qū)域從基礎(chǔ)對象形狀擴展其他形狀。 每個形狀都可以有自己的屬性,像矩形這樣的函數(shù)可以具有屬性length,width,area作為屬性,printArea和calculateArea作為它的函數(shù)。

          創(chuàng)建一個簡單的類

          下面顯示了具有三個屬性length,width和area的矩形的簡單類實現(xiàn)。 它還有一個printArea函數(shù)來打印計算區(qū)域面積。

          -- 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

          創(chuàng)建一個對象 創(chuàng)建對象是為類實例分配內(nèi)存的過程。 每個對象都有自己的內(nèi)存并共享公共類數(shù)據(jù)。

          r=Rectangle:new(nil,10,20)

          訪問屬性 可以使用點(.)運算符訪問類中的屬性,如下所示 -

          print(r.length)

          訪問成員函數(shù)

          使用帶有對象的冒號(:)運算符訪問成員函數(shù),如下所示 -

          r=Rectangle:new(nil,10,20)
          r:printArea()

          分配內(nèi)存并設(shè)置初始值。可以將初始化過程與其他面向?qū)ο笳Z言中的構(gòu)造函數(shù)進行比較。 它只是一個能夠設(shè)置如上所示的值的函數(shù)。

          完整的例子 下面來看一下在Lua中使用面向?qū)ο蟮耐暾纠4a如下 -

          -- 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

          Lua繼承

          繼承是將簡單的基礎(chǔ)對象(如形狀)擴展為矩形,正方形等的過程。 它經(jīng)常在現(xiàn)實世界中用于共享和擴展基本屬性和功能。

          下面來看一個簡單的類擴展。有一個如下所示的類,

          -- 元類
          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

          覆蓋基類函數(shù)

          可以覆蓋基類函數(shù)而不是在基類中使用函數(shù),派生類可以有自己的實現(xiàn),如下所示 -

          -- Derived class method printArea
          function Square:printArea ()
              print("The area of square is ",self.area)
          end

          繼承完整示例 在另一個new方法的幫助下,使用metatables,擴展Lua中的簡單類實現(xiàn),如上所示。 基類的所有成員變量和函數(shù)都保留在派生類中。

          -- 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

          在上面的例子中,創(chuàng)建了兩個派生類 - 基類Square和Rectangle。并在派生類中覆蓋基類的函數(shù)。 在此示例中,派生類會覆蓋函數(shù)printArea。

          Lua Web編程

          Lua是一種高度靈活的語言,通常用于多種平臺,包括Web應用程序。 開普勒(Kepler)社區(qū)成立于2004年,旨在為Lua提供開源Web組件。

          盡管已經(jīng)有很多Lua Web開發(fā)是使用Lua的Web框架,但這里主要介紹和使用Kepler社區(qū)提供的組件。

          Lua常用應用程序和框架

          • Orbit - Orbit是Lua的MVC Web框架,基于WSAPI。
          • WSAPI - WSAPI是從Lua Web應用程序抽象Web主機服務(wù)器的API,是許多項目的基礎(chǔ)。
          • Xavante - 是一個提供WSAPI接口的Lua Web服務(wù)器。
          • Sputnik - 是一個wiki/CMS,通過用于幽默和娛樂的Kepler項目的WSAPI開發(fā)。
          • CGILua - 基于WSAPI提供LuaPages和LuaScripts網(wǎng)頁創(chuàng)建,但不再受支持。 請改用Orbit,Sputnik或WSAPI。

          在本教程中,將讓您了解Lua可以執(zhí)行的操作,并了解有關(guān)其安裝和使用的更多信息,請參閱kepler網(wǎng)站。

          Orbit

          Orbit是Lua的MVC Web框架。 它完全放棄了CGILua“腳本”模型,轉(zhuǎn)而支持應用程序,每個Orbit應用程序都可以放在一個文件中,但如果需要,可以將它分成多個文件。

          所有Orbit應用程序都遵循WSAPI協(xié)議,因此它們目前與Xavante,CGI和Fastcgi一起使用。 它包含一個啟動程序,可以輕松啟動Xavante實例進行開發(fā)。

          安裝Orbit的最簡單方法是使用LuaRocks。 Luarocks是安裝Orbit的安裝的命令。 為此,需要先安裝LuaRocks。

          如果尚未安裝所有依賴項,則以下是在Unix/Linux 環(huán)境中設(shè)置Orbit時要遵循的步驟。

          安裝Apache 連接到服務(wù)器,使用以下命令安裝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,F(xiàn)CGI,Orbit和Xavante

          $ sudo luarocks install orbit
          $ sudo luarocks install wsapi-xavante
          $ sudo luarocks install wsapi-fcgi

          設(shè)置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>

          重新啟動服務(wù)器以確保所做的更改生效。 要啟用應用程序,需要將+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

          現(xiàn)在,可以啟動Web瀏覽器了。 轉(zhuǎn)到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())

          創(chuàng)建表單

          一個簡單的表單示例,如下所示 -

          #!/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充當許多項目的基礎(chǔ),并在其中嵌入了多個功能。 可以使用WSAPI并支持以下平臺 -

          • Windows
          • UNIX類系統(tǒng)

          WSAPI支持的服務(wù)器和接口包括 -

          • CGI
          • FastCGI
          • Xavante

          WSAPI提供了許多庫,可以更輕松地使用Lua進行Web編程。 Lua中的一些受支持的功能包括,

          • 請求處理
          • 輸出緩沖
          • 認證
          • 文件上傳
          • 請求隔離
          • 并發(fā)傳輸

          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 頁面。可以看到協(xié)同程序的用法,它可以將語句返回到調(diào)用函數(shù)。 最后,返回html狀態(tài)代碼(200),標題和html頁面。

          Xavante

          Xavante是一個Lua HTTP 1.1 Web服務(wù)器,它使用基于URI映射處理程序的模塊化體系結(jié)構(gòu)。 Xavante目前提供以下功能 -

          • 文件處理程序
          • 重定向處理程序
          • WSAPI處理程序

          文件處理程序用于常規(guī)文件。重定向處理程序啟用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的調(diào)用將更改為如下所示 -

          xavante.HTTP{
          		server={host="*", port=8080},
          		defaultHost={},
          		virtualhosts={
          			["www.yiibai.com"]=simplerules
          		}
          }

          Lua的Web組件

          • Copas - 一個基于協(xié)同程序的調(diào)度程序,可以由TCP/IP服務(wù)器使用。
          • Cosmo - 一個“安全模板”引擎,可以保護應用程序免受模板中任意代碼的攻擊。
          • Coxpcall - 它將Lua native pcall和xpcall與coroutine兼容。
          • LuaFileSystem - 一種訪問底層目錄結(jié)構(gòu)和文件屬性的可移植方式。
          • Rings - 一個程序庫,提供從Lua內(nèi)部創(chuàng)建新的Lua狀態(tài)的方法。

          結(jié)束說明

          有許多基于Lua的Web框架和組件可供使用,根據(jù)需要選擇它。 還有其他可用的Web框架,包括以下內(nèi)容 -

          • Moonstalk可以高效地開發(fā)和托管使用Lua語言構(gòu)建的動態(tài)生成的基于Web的項目; 從基本頁面到復雜的應用程序。
          • Lapis,這是一個使用MoonScript(或Lua)構(gòu)建Web應用程序的框架,它在一個名為OpenResty的Nginx定制版本中運行。
          • Lua Server Pages 是一個Lua腳本引擎插件,它打破了嵌入式Web開發(fā)的任何其他方法,為傳統(tǒng)的C服務(wù)器頁面提供了顯著的捷徑。

          這些Web框架可在Web應用程序中充分利用,以執(zhí)行強大的操作功能。

          Lua MySQL操作

          對于簡單的數(shù)據(jù)操作,可使用文件處理。但有時,這些文件操作可能效率不高,可擴展且功能強大。 為此經(jīng)常需要使用數(shù)據(jù)庫。 LuaSQL是一個從Lua到許多數(shù)據(jù)庫管理系統(tǒng)的簡單接口。 LuaSQL是一個庫,它為不同類型的SQL提供支持。 包括 -

          • SQLite
          • Mysql
          • ODBC

          在本教程中,將介紹在Lua中如何使用MySQL和SQLite數(shù)據(jù)庫操作處理。它為兩者使用通用接口,并且也可以將此實現(xiàn)移植到其他類型的數(shù)據(jù)庫。 首先來看看如何在MySQL中進行操作。

          MySQL數(shù)據(jù)庫安裝設(shè)置

          要使以下示例按預期工作,需要初始數(shù)據(jù)庫安裝設(shè)置。 假設(shè)如下 -

          • 安裝并設(shè)置MySQL使用默認用戶:root,密碼為:123456。
          • 創(chuàng)建數(shù)據(jù)庫:test
          • 可通過學習MySQL教程來了解MySQL基礎(chǔ)知識。

          導入MySQL

          假設(shè)Lua實現(xiàn)正確完成,使用一個簡單的require語句來導入sqlite庫。

          mysql=require "luasql.mysql"

          變量mysql將通過引用主mysql表來提供對函數(shù)的訪問。

          設(shè)置連接

          通過啟動MySQL環(huán)境,然后為環(huán)境創(chuàng)建連接。 如下所示 -

          local env=mysql.mysql()
          local conn=env:connect('test','root','123456')

          上述連接將連接到現(xiàn)有的MySQL文件,并與新創(chuàng)建的文件建立連接。

          執(zhí)行函數(shù)

          連接提供了一個簡單的執(zhí)行函數(shù),可以幫助我們執(zhí)行創(chuàng)建,插入,刪除,更新等操作完成所有數(shù)據(jù)庫操作。 語法如下所示 -

          conn:execute([[ 'MySQLSTATEMENT' ]])

          在上面的語法中,需要確保conn是打開的,以及MySQL連接成功,并用正確的SQL語句替換'MySQLSTATEMENT'。

          創(chuàng)建表示例

          下面顯示了一個簡單的create table示例。 它創(chuàng)建一個表,其中包含兩個字段參數(shù): 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 )

          運行上述程序時,將創(chuàng)建一個名為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語句而言,我們需要循環(huán)遍歷每一行并提取所需的數(shù)據(jù)。一個簡單的選擇語句如下所示 -

          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語句返回的游標的幫助下,可以遍歷表響應并獲取所需的選擇數(shù)據(jù)。

          一個完整的例子

          下面給出了包括所有上述語句的完整示例 -

          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

          執(zhí)行事務(wù)

          事務(wù)是一種確保數(shù)據(jù)一致性的機制。事務(wù)具有以下四個屬性 -

          • 原子性 - 事務(wù)完成或根本沒有任何事情發(fā)生。
          • 一致性 - 事務(wù)必須以一致狀態(tài)啟動,并使系統(tǒng)保持一致狀態(tài)。
          • 隔離性 - 在當前事務(wù)之外不可見事務(wù)的中間結(jié)果。
          • 持久性 - 提交事務(wù)后,即使系統(tǒng)出現(xiàn)故障,影響也會持續(xù)存在。

          事務(wù)以START TRANSACTION開始; 并以commit或rollback語句結(jié)束。

          開始事務(wù)

          要啟動事務(wù),需要在Lua中執(zhí)行以下語句,假設(shè)conn是一個打開的MySQL連接。

          conn:execute([[START TRANSACTION;]])

          回滾事務(wù)

          需要執(zhí)行以下語句來回滾執(zhí)行啟動事務(wù)后所做的更改。

          conn:execute([[ROLLBACK;]])

          提交事務(wù)

          需要執(zhí)行以下語句來提交啟動事務(wù)執(zhí)行后所做的更改。

          conn:execute([[COMMIT;]])

          在上面和后面的章節(jié)中已經(jīng)了解了MySQL的基本SQL操作。 記住事務(wù),雖然這里沒有為SQLite3解釋,但同樣的語句也適用于SQLite3。

          Lua游戲編程

          Lua由于其簡單的語言結(jié)構(gòu)和語法,它廣泛地用于游戲引擎開發(fā)中。 垃圾收集功能通常在由于使用豐富的圖形而消耗大量內(nèi)存的游戲中非常有用。 一些使用Lua的游戲引擎包括 -

          • Corona SDK
          • Gideros Mobile
          • ShiVa3D
          • Moai SDK
          • LOVE
          • CryEngine

          這些游戲引擎中的每一個都基于Lua,并且每個引擎中都有一組豐富的API。 接下來將簡要介紹每種引擎功能。

          1. Corona SDK

          Corona SDK是一款支持iPhone,iPad和Android平臺的跨平臺移動游戲引擎。 有一個免費版本的Corona SDK,可用于功能有限的小游戲。可以在需要時升級到其他版本。

          Corona SDK提供了許多功能,包括以下功能 -

          • 物理和碰撞處理API
          • Web和網(wǎng)絡(luò)API
          • 游戲網(wǎng)絡(luò)API
          • 廣告API
          • 分析API
          • 數(shù)據(jù)庫和文件系統(tǒng)API
          • 加密和數(shù)學API
          • 音頻和媒體API

          使用上述API開發(fā)應用程序比使用iOS和Android單獨使用本機API更容易,更快捷。

          2. Gideros Mobile

          Gideros提供跨平臺SDK來為iOS和Android創(chuàng)建游戲,可以免費使用Gideros。 Gideoros的一些顯著優(yōu)勢包括 -

          • 開發(fā)IDE - 它提供了自己的IDE,可以更輕松地開發(fā)Gideros應用程序。
          • 即時測試 - 在開發(fā)游戲時,只需1秒即可通過Wifi在真實設(shè)備上進行測試。無需在導出或部署過程中浪費時間。
          • 插件 - 使用插件輕松擴展核心。 導入現(xiàn)有的(C,C++,Java或Obj-C)代碼,綁定到Lua并直接解釋它們。已經(jīng)開發(fā)了許多開源插件并且可以隨時使用。
          • 干凈OOP方法 - Gideros提供自己的類系統(tǒng),包含所有基本的OOP標準,使您能夠為未來的任何游戲編寫干凈且可重復使用的代碼。
          • 原生速度 - 基于C/C++和OpenGL開發(fā),游戲以原生速度運行,并充分利用下面的CPU和GPU的強大功能。

          3. ShiVa3D

          ShiVa3D是3D游戲引擎之一,它提供了一個圖形編輯器,旨在為Web,控制臺和移動設(shè)備創(chuàng)建應用程序和視頻游戲。 它支持多種平臺,包括Windows,Mac,Linux,iOS,Android,BlackBerry,Palm OS,Wii和WebOS。

          一些主要功能包括 -

          • 標準插件
          • 網(wǎng)格修改API
          • IDE
          • 內(nèi)置地形,海洋和動畫編輯器
          • ODE物理引擎支持
          • 完整的光照貼圖控件
          • 實時預覽材料,粒子,軌跡和HUD
          • Collada交換格式支持

          Shiva3d的網(wǎng)絡(luò)版是完全免費的,其他版本可能需要收費。

          4. Moai SDK

          Moai SDK是一款支持iPhone,iPad和Android平臺的跨平臺移動游戲引擎。 Moai平臺最初由Moai SDK(一種開源游戲引擎)和Moai Cloud(一種用于托管和部署游戲服務(wù)的服務(wù)的云平臺)組成。 現(xiàn)在Moai Cloud已關(guān)閉,只有游戲引擎可用。

          Moai SDK可在多種平臺上運行,包括iOS,Android,Chrome,Windows,Mac和Linux。

          5. LOVE

          LOVE是一個可用于制作2D游戲的框架,它是免費和開源的。 它支持Windows,Mac OS X和Linux平臺。

          它提供了多種功能,包括:

          • 音頻API
          • 文件系統(tǒng)API
          • 鍵盤和操縱桿API
          • 數(shù)學API
          • 窗口和鼠標API
          • 物理API
          • 系統(tǒng)和計時器API

          6. CryEngine

          CryEngine是由德國游戲開發(fā)商Crytek開發(fā)的游戲引擎。 它已從第1代發(fā)展到第4代,是一種先進的開發(fā)解決方案。 它支持PC,Xbox 360,PlayStation3和WiiU游戲。

          它提供了多種功能,包括:

          • 視覺效果,如自然光和動態(tài)軟陰影,實時動態(tài)全局照明,光傳播體積,粒子著色,曲面細分等。
          • 人物動畫系統(tǒng)與人物個性化系統(tǒng)。
          • 參數(shù)化動畫和獨特的專用面部動畫編輯器。
          • AI系統(tǒng),如多層導航網(wǎng)格和戰(zhàn)術(shù)點系統(tǒng)。 還提供設(shè)計師友好的AI編輯系統(tǒng)。
          • 在游戲混合和分析,數(shù)據(jù)驅(qū)動的聲音系統(tǒng)動態(tài)聲音和交互式音樂等。
          • 物理特征如程序變形和高級繩索物理。

          這些游戲SDK/框架中的每一個都有各自的優(yōu)缺點。 在它們之間進行適當?shù)倪x擇可以讓工作更輕松,并且可以更好地利用它。 因此,在使用它之前,需要了解游戲的要求,然后分析哪些能滿足需求,然后才能決定使用哪一個。

          Lua標準庫

          Lua標準庫提供了一組豐富的函數(shù),這些函數(shù)直接使用C語言API實現(xiàn),并使用Lua編程語言構(gòu)建。這些庫提供Lua編程語言中的服務(wù)以及文件和數(shù)據(jù)庫操作之外的服務(wù)。

          這些在官方C語言API中構(gòu)建的標準庫作為單獨的C模塊提供。它包括以下內(nèi)容 -

          • 基本庫,包括協(xié)程子庫
          • 模塊庫
          • 字符串操作
          • 表操作
          • 數(shù)學庫
          • 文件輸入和輸出
          • 操作系統(tǒng)設(shè)施
          • 調(diào)試工具

          1. 基礎(chǔ)庫

          我們在整個教程中使用了各種主題下的基本庫。 下表提供了相關(guān)頁面的鏈接,并列出了本Lua教程各部分所涵蓋的功能。

          編號

          庫/方法

          作用

          1

          錯誤處理

          包括錯誤處理函數(shù),如斷言, 錯誤,如Lua錯誤處理中所述。

          2

          內(nèi)存管理

          包括與垃圾收集相關(guān)的自動內(nèi)存管理功能, 如Lua垃圾收集中所述。

          3

          dofile ([filename])

          它打開文件并以塊的形式執(zhí)行文件的內(nèi)容。

          4

          _G

          因此是保存全局環(huán)境的全局變量(即_G._G=_G)。

          5

          getfenv ([f])

          返回函數(shù)使用的當前環(huán)境。

          6

          getmetatable (object)

          如果object沒有metatable, 則返回nil。 否則,如果object的metatable具有__metatable字段,

          7

          ipairs (t)

          此函數(shù)獲取表的索引和值。

          8

          load (func [, chunkname])

          使用函數(shù)func加載一個塊來獲取它的碎片。

          9

          loadfile ([filename]))

          與load類似,但是如果沒有給出文件名,則從文件filename或標準輸入中獲取塊。

          10

          loadstring (string [, chunkname])

          與load函數(shù)類似,但從給定的字符串中獲取塊。

          11

          next (table [, index])

          允許程序遍歷表的所有字段。

          12

          pairs (t)

          暫停正在運行的協(xié)同程序。

          13

          print (...)

          打印給定的參數(shù)值。

          14

          rawequal (v1, v2)

          檢查v1是否等于v2,而不調(diào)用任何無方法。 返回一個布爾值。

          15

          rawget (table, index)

          獲取table [index]的值, 而不調(diào)用任何方法。table必須是表; index可以是任何值。

          16

          rawset (table, index, value)

          將table [index]的值設(shè)置為value,而不調(diào)用任何方法。

          17

          select (index, ...)

          如果index是數(shù)字,則返回參數(shù)編號索引后的所有參數(shù)。

          18

          setfenv (f, table)

          設(shè)置給定函數(shù)使用的環(huán)境。

          19

          setmetatable (table, metatable)

          設(shè)置給定表的元表。

          20

          tonumber (e [, base])

          嘗試將參數(shù)轉(zhuǎn)換為數(shù)字。

          21

          tostring (e)

          接收任何類型的參數(shù)并將其轉(zhuǎn)換為合理格式的字符串。

          22

          type (v)

          返回唯一參數(shù)的類型,編碼為字符串。

          23

          unpack (list [, i [, j]])

          返回給定表中的元素。

          24

          _VERSION

          包含當前解釋器版本的字符串的全局變量(不是函數(shù))。

          25

          協(xié)同程序

          包括Lua協(xié)同程序中解釋的協(xié)程操作功能。

          2. 模塊庫

          模塊庫提供了在Lua中加載模塊的基本功能。 它直接在全局環(huán)境中導出一個函數(shù):require。 其他所有內(nèi)容都在表包中導出。 有關(guān)模塊庫的詳細信息,請參見前面的Lua模塊教程。

          3. 字符串操作

          Lua提供了豐富的字符串操作功能。 前面的Lua字符串教程詳細中介紹了這一點。

          4. 表操作

          Lua幾乎在其所有操作中都依賴于表。 前面的Lua表教程詳中細介紹了這一點。

          5. 文件輸入和輸出

          經(jīng)常需要編程中的數(shù)據(jù)存儲工具,這是由Lua中的文件I/O的標準庫函數(shù)提供的。 它在前面的Lua 文件操作教程中討論過。

          6. 調(diào)試工具

          Lua提供了一個調(diào)試庫,它提供了創(chuàng)建自己的調(diào)試器的所有原始函數(shù)。 在前面的Lua調(diào)試教程中討論過了。

          Lua數(shù)學庫

          經(jīng)常需要在科學和工程計算中進行數(shù)學運算,可以使用標準的Lua庫數(shù)學來實現(xiàn)。 數(shù)學庫中可用的函數(shù)列表如下表所示 -

          編號

          庫或方法

          描述

          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的反正切(以弧度表示),但使用兩個參數(shù)的符號來查找結(jié)果的象限(它也正確處理x為零的情況。)

          6

          math.ceil(x)

          返回大于或等于x的最小整數(shù)。

          7

          math.cos(x)

          返回x的余弦值(假設(shè)為弧度)。

          8

          math.cosh(x)

          返回x的雙曲余弦值。

          9

          math.deg(x)

          以度為單位返回角度x(以弧度表示)。

          10

          math.exp(x)

          返回值e的x次冪。

          11

          math.floor(x)

          返回小于或等于x的最大整數(shù)。

          12

          math.fmod(x,y)

          返回x除以y的余數(shù),將商舍入為零。

          13

          math.frexp(x)

          返回m和e,使得x=m2e,e是整數(shù),m的絕對值在[0.5,1]范圍內(nèi)(或者當x為零時為零)。

          14

          math.huge

          HUGE_VAL值是一個大于或等于任何其他數(shù)值的值。

          15

          math.ldexp(m, e)

          返回m2e(e是一個整數(shù))。

          16

          math.log(x)

          返回x的自然對數(shù)。

          17

          math.log10(x)

          返回x的以10為底的對數(shù)。

          18

          math.max(x,...)

          返回參數(shù)中的最大值。

          19

          math.min(x,...)

          返回參數(shù)中的最小值。

          20

          math.modf(x)

          返回兩個數(shù)字,x的整數(shù)部分和x的小數(shù)部分。

          21

          math.pi

          pi的值。

          22

          math.pow(x,y)

          返回x的y方。(也可以使用表達式x ^ y來計算此值。)

          23

          math.rad(x)

          以弧度為單位返回角度x(以度為單位)。

          24

          math.random([m [, n]])

          此函數(shù)是ANSI C提供的簡單偽隨機生成器函數(shù)rand的接口。

          25

          math.randomseed(x)

          將x設(shè)置為偽隨機生成器的“種子”:相等的種子產(chǎn)生相等的數(shù)字序列。

          26

          math.sin(x)

          返回x的正弦值(假設(shè)為弧度)。

          27

          math.sinh(x)

          返回x的雙曲正弦值。

          28

          math.sqrt(x)

          返回x的平方根。(也可以使用表達式x ^ 0.5來計算此值。)

          29

          math.tan(x)

          返回x的正切(假設(shè)為弧度)。

          30

          math.tanh(x)

          返回x的雙曲正切值。

          三角函數(shù)

          使用三角函數(shù)的簡單示例如下所示-

          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

          其他常見的數(shù)學函數(shù)

          使用常見數(shù)學函數(shù)的簡單示例如下所示-

          -- 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

          上面的例子只是一些常見的例子,可以根據(jù)需要使用數(shù)學庫,所以嘗試使用所有的函數(shù)以更熟悉運用。

          Lua操作系統(tǒng)工具

          在任何應用程序中,通常都需要訪問操作系統(tǒng)級功能,并且可以使用操作系統(tǒng)庫。 可用功能列表如下表所示。

          編號

          庫或方法

          描述

          1

          os.clock()

          返回程序使用的CPU時間(以秒為單位)的近似值。

          2

          os.date([format[, time]])

          返回包含日期和時間的字符串或表,根據(jù)給定的字符串格式進行格式化。

          3

          os.difftime(t2,t1)

          返回從時間t1到時間t2的秒數(shù)。在POSIX,Windows和其他一些系統(tǒng)中,恰好是t2-t1的值。

          4

          os.execute([command])

          此功能相當于ANSI C功能系統(tǒng)。 它傳遞要由操作系統(tǒng)shell執(zhí)行的命令。 如果命令成功終止,則第一個結(jié)果為true,否則為nil。

          5

          os.exit([code[, close])

          調(diào)用ANSI C函數(shù)出口以終止宿主程序。 如果code為true,則返回狀態(tài)為EXIT_SUCCESS; 如果code為false,則返回狀態(tài)為EXIT_FAILURE; 如果code是數(shù)字,則返回的狀態(tài)是此數(shù)字。

          6

          os.getenv(varname)

          返回進程環(huán)境變量varname的值,如果未定義變量,則返回nil。

          7

          os.remove(filename)

          使用給定名稱刪除文件(或POSIX系統(tǒng)上的空目錄)。 如果此函數(shù)失敗,則返回nil,以及描述錯誤和錯誤代碼的字符串。

          8

          os.rename(oldname, newname)

          將名為oldname的文件或目錄重命名為newname。 如果此函數(shù)失敗,則返回nil,以及描述錯誤和錯誤代碼的字符串。

          9

          os.setlocale(locale [,category])

          設(shè)置程序的當前區(qū)域設(shè)置。 locale是一個依賴于系統(tǒng)的字符串,用于指定語言環(huán)境; category是一個可選字符串,用于描述要更改的類別:all,collate,ctype,currency,numeric或time; 默認類別(category)是"all"。該函數(shù)返回新語言環(huán)境的名稱,如果無法滿足請求,則返回nil。

          10

          os.time([table])

          返回不帶參數(shù)調(diào)用的當前時間,或表示給定表指定的日期和時間的時間。 此表必須包含字段年,月和日,并且可能包含字段小時(默認值為12),分鐘(默認值為0),秒(默認值為0)和isdst(默認值為nil)。 有關(guān)這些字段的說明,請參見os.date函數(shù)。

          11

          os.tmpname()

          返回一個文件名,該文件名可用于臨時文件。 文件必須在使用前顯式打開,并在不再需要時顯式刪除。

          常見的OS功能

          使用常見數(shù)學函數(shù)的簡單示例如下所示 -

          -- 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

          上面的例子只是一些常見的例子,可根據(jù)自己的需要使用OS庫,建議嘗試使用所有的功能以便更加熟悉。像remove這樣的函數(shù)有助于刪除文件,執(zhí)行有助于于執(zhí)行OS命令。

          ldoc

          參考

          http://stevedonovan.github.io/ldoc/manual/doc.md.html
          LDOC 入門指南 -- 重點是里面的安裝 - ZhYQ_note - 博客園

          說明

          LDoc遵循JavaDoc等文檔生成工具的傳統(tǒng),即只有doc comment可以被解析,這種doc comment必須以三個-開頭

          安裝方法

          brew install luarocks -v
          luarocks install Ldoc

          使用方法

          很多好學的同學都喜歡在業(yè)余時間充電,與其蹲廁所看垃圾八卦新聞,還不如學習的有用的知識充實自己,LUA是一種比較簡單 的計算機軟件語言,下面一些簡易知識可以幫助你輕松入門。

          一、lua編寫和調(diào)試

          Lua是一個免費開源的語言,官網(wǎng)是這個http://www.lua.org/download.html,可以到這里下載源碼或者編譯好的版本。

          然后編輯器(IDE),可以用Notepad++,或者Sublime,Java碼農(nóng)可以用eclipse的插件Luaclipse,c#碼農(nóng)可以用vs。

          本人需要用N個電腦,經(jīng)常有一點簡單的代碼想調(diào)試一下,又懶得搞環(huán)境,就用在線運行環(huán)境,這個算是比較好用的一個http://www.dooccn.com/lua,推薦使用。

          二、lua語法

          1、Lua數(shù)據(jù)類型

          • nil

          • boolean:布爾值, 也就是true/false。對于數(shù)字的真假判斷跟其他語言有一點點不一樣的,0為真(其他語言里面0一般為假)

          • number

          • string

          • userdata

          • function:函數(shù)

          • thread

          • table:lua里面沒有array和map的概念,都用map來表示,這是lua特有的一個類型,下面會重點介紹一下

          使用type函數(shù)可以查看某個變量的類型,如:

          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函數(shù)是把變量打印到控制臺,如果在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也是真,別忘了
          --基本跟其他語言沒有任何區(qū)別,不廢話了

          4、關(guān)系運算

          ==等于~=不等于,前面那個是波浪線
          >大于=大于等于<小于
          <=小于等于

          5、局部變量和全局變量

          do
           a=1--不帶local就是全局變量
           local b=2--帶local就是局部變量
          endprint(a)-->1print(b)-->nil

          6、if/else

          if true thenelseif true thenelseend

          7、while循環(huán)

          local i=1while a[i] do
           print(a[i])
           i=i + 1end

          8、for循環(huán)

          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是結(jié)束循環(huán),return是返回,lua不支持continue

          11、注釋

          上面我們已經(jīng)使用了較多的注釋,這里重復一下

          --單行注釋local a--[[
          多行注釋
          local a
          ]]--local b

          12、函數(shù)

          來看一下常規(guī)的,求兩個數(shù)字中最大的

          --[[ 函數(shù)返回兩個值的最大值 --]]function max(num1, num2)
           if (num1 > num2) then
           result=num1; else
           result=num2; end
           return result;
          end-- 調(diào)用函數(shù)print("兩值比較最大值為 ",max(10,4))print("兩值比較最大值為 ",max(5,6))--運行結(jié)果如下;兩值比較最大值為 10兩值比較最大值為 6

          lua的函數(shù)可以返回多個值

          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也支持傳多個參數(shù),一次性全打印出來,中間用空格分隔

          沒有名字的函數(shù)叫“匿名函數(shù)”,效果跟上面一樣的

          --返回自己的坐標local x,y,z=function(unit)
           if unit=="player" then
           return 1,2,3
           endendprint(x,y,z)

          lua的函數(shù)可以接收任意個數(shù)的參數(shù)


          主站蜘蛛池模板: 久久精品无码一区二区三区| 高清一区二区三区日本久| 亚洲av成人一区二区三区在线观看| 中文字幕亚洲一区二区va在线| 97精品一区二区视频在线观看| 中文字幕aⅴ人妻一区二区| 香蕉久久ac一区二区三区| 久久精品一区二区三区AV| 国产乱码精品一区二区三区中文 | 亚洲欧美成人一区二区三区| 日韩免费视频一区二区| 久久精品一区二区影院| 免费人人潮人人爽一区二区| 亚洲AV无码一区二区三区性色| 亚洲视频一区网站| 91在线一区二区| 精品三级AV无码一区| 亚洲区精品久久一区二区三区| 亚洲第一区香蕉_国产a| 一区二区三区日韩精品| 日韩高清国产一区在线| 日本不卡在线一区二区三区视频 | 波多野结衣一区二区三区88 | 国产午夜精品一区二区三区小说 | 久久综合亚洲色一区二区三区| 国产在线无码一区二区三区视频| 久久精品国产一区二区电影| 国产美女露脸口爆吞精一区二区| 黑人一区二区三区中文字幕| 欧洲精品码一区二区三区免费看| 立川理惠在线播放一区| 国产精品区AV一区二区| 精品一区二区三区在线视频| 麻豆AV一区二区三区久久| 亚洲av无码一区二区三区天堂古代| 无码人妻精品一区二区蜜桃网站 | 日韩免费视频一区二区| 无码乱人伦一区二区亚洲一| 中文字幕日韩一区二区不卡| 日韩精品一区二区三区国语自制 | 成人影片一区免费观看|