整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Nginx Lua編程基礎

          Nginx Lua編程基礎

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

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

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

          ngx_lua是Nginx的一個擴展模塊,將Lua VM嵌入Nginx,請求時創建一個VM,請求結束時回收VM,這樣就可以在Nginx內部運行Lua腳本,使得Nginx變成一個Web容器。以OpenResty為例,其提供了一些常用的ngx_lua開發模塊:

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

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

          Nginx Lua的執行原理

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

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

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

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

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

          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代碼塊,常用于執行一些定時任務。
          • init_worker_by_lua_file:Worker進程啟動時掛載的Lua文件,常用于執行一些定時任務。
          • set_by_lua:類似于rewrite模塊的set指令,將Lua代碼塊的返回結果設置在Nginx的變量中。
          • set_by_lua_file:同上,執行的是腳本Lua腳本文件。
          • rewrite_by_lua:執行在rewrite階段的Lua代碼塊,完成轉發、重定向、緩存等功能。
          • rewrite_by_lua_file:同上,執行的是Lua腳本文件。
          • access_by_lua:執行在access階段的Lua代碼塊,完成IP準入、接口權限等功能。
          • access_by_lua_file:同上,執行的是Lua腳本文件。
          • content_by_lua:執行在content階段的Lua代碼塊,執行結果將作為請求響應的內容。
          • content_by_lua_file:同上,執行的是Lua腳本文件。
          • content_by_lua_block:content_by_lua的升級款,在一對花括號中編寫Lua代碼,而不需要做特殊字符轉譯。
          • header_filter_by_lua:響應頭部過濾處理的Lua代碼塊,可以用于添加設置響應頭部信息,如Cookie相關屬性。
          • body_filter_by_lua:響應體過濾處理的Lua代碼塊,例如加密響應體。
          • log_by_lua:異步完成日志記錄的Lua代碼塊,例如既可以在本地記錄日志,也可以記錄到ETL集群。

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

          常用配置指令

          • lua_package_path指令:用于設置".lua"外部庫的搜索路徑,此指令的上下文為http配置塊,默認值為LUA_PATH環境變量內容或者lua編譯的默認值。
            • 格式:lua_package_path lua-style-path-str。
            • lua_package_cpath指令:用于設置Lua的C語言塊外部庫".so"(Linux)或".dll"(Windows)的搜索路徑,此指令的上下文為http配置塊。
            • 格式:lua_package_cpath lua-style-cpath-str
          http {
            ...
            #設置“.lua”外部庫的搜索路徑,此指令的上下文為http配置塊
          	#";;"常用于表示原始的搜索路徑
          	lua_package_path	"/foo/bar/?.lua;/blah/?.lua;;";
          	lua_package_cpath	"/usr/local/openresty/lualib/?/?.so;/usr/local/openresty/lualib/?.so;;";
          }
          

          對于以上兩個指令,OpenResty可以在搜索路徑中使用插值變量。例如,可以使用插值變量$prefix或${prefix}獲取虛擬服務器server的前綴路徑,server的前綴路徑通常在Nginx服務器啟動時通過-p PATH命令在指定。

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

          格式為:init_by_lua lua-script-str。

          • lua_load_cache指令:用于啟用或禁止Lua腳本緩存??梢允褂玫纳舷挛臑閔ttp、server、location配置塊。默認開啟。

          格式為:lua_code_cache on | off

          http {
            ...
          	#項目初始化
            init_by_lua_file	conf/luaScript/initial/loading_config.lua;
            	
            #調試模式,關閉lua腳本緩存
            lua_code_cache on;
            ...
          }

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

          • set_by_lua指令:將Lua腳本塊的返回結果設置在Nginx變量中。

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

          location /set_by_lua_demo {
          	#set 指令定義兩個Nginx變量
            set $foo 1;
            set $bar 2;
            			
            #調用Lua內聯代碼,將結果放入Nginx變量$sum
            #Lua腳本的含義是,將兩個輸入參數$foo、$bar累積起來,然后相加的結果設置Nginx變量$sum中
            set_by_lua $sum 'return tonumber(ngx.arg[1]) + tonumber(ngx.arg[2])' $foo $bar;
            
            echo "$foo + $bar=$sum";
          }

          運行結果:

          ?  work curl http://localhost/set_by_lua_demo
          1 + 2=3
          • access_by_lua指令:執行在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";
          }

          運行結果:

          ?  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指令:用于設置執行在content階段的Lua代碼塊,執行結果將作為請求響應的內容。該指令用于location上下文。

          格式為:content_by_lua lua-script-str

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

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

          運行結果:

          ?  work curl http://localhost/errorLog
          錯誤日志調用成功
          ?  work curl http://localhost/infoLog 
          業務日志調用成功
          ?  work curl http://localhost/debugLog
          調試日志調用成功

          Nginx Lua的內置常量和變量

          內置變量

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

          內置常量

          內置常量基本是見名知意的,可以根據后面的實戰案例,加深理解。

          核心常量

            • 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狀態碼常量

            • 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基礎到此結束,下一篇開始實戰!并在實戰中掌握基礎。


          ua之參考

          Lua基礎語法 -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標識符是用于標識變量,函數或任何其他用戶定義項的名稱。 標識符以字母A到Z或a到z或下劃線_開頭,后跟零個或多個字母,下劃線和數字(0到9)。

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

          mohd      zara   abc  move_name  a_123
          myname50 _temp   j    a23b9      retVal

          Lua關鍵字

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

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

          Lua變量

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

          Lua數據類型

          編號

          值類型

          描述

          1

          nil

          用于區分值與某些數據或沒有(nil)數據。

          2

          boolean

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

          3

          number

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

          4

          string

          表示字符數組。

          5

          function

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

          6

          userdata

          表示任意C語言數據。

          7

          thread

          表示獨立的執行線程,它用于實現協同程序。

          8

          table

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

          Lua運算符

          • 算術運算符
          • 關系運算符
          • 邏輯運算符
          • 其它運算符

          算術運算符

          運算符

          描述

          示例

          +

          相加兩個操作數

          A + B=30

          -

          從第一個減去第二個操作數

          A - B=-10

          *

          將兩個操作數相乘

          A * B=200

          /

          用除分子除以分子

          B / A=2

          %

          模數運算符,整數除法后的余數

          B % A=0

          ^

          指數運算符取指數冪值

          A^2=100

          -

          一元,充當否定

          -A=-10

          關系運算符

          運算符

          描述

          示例

          ==

          檢查兩個操作數的值是否相等,如果相等,則條件變為真。

          (A==B)結果為false

          ~=

          檢查兩個操作數的值是否相等,如果值不相等則條件變為true。

          (A ~=B)結果為true

          >

          檢查左操作數的值是否大于右操作數的值,如果是,則條件變為true。

          (A > B)結果為false

          <

          檢查左操作數的值是否小于右操作數的值,如果是,則條件變為true。

          (A < B)結果為true

          >=

          檢查左操作數的值是否大于或等于右操作數的值,如果是,則條件變為true。

          (A >=B)結果為false

          <=

          檢查左操作數的值是否小于或等于右操作數的值,如果是,則條件變為true。

          (A <=B)結果為true

          邏輯運算符

          運算符

          描述

          示例

          and

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

          (A and B) 結果為false

          or

          邏輯或運算符。 如果兩個操作數中的任何一個不為零,則條件變為真。

          (A or B) 結果為true

          not

          邏輯非運算符。用于反轉其操作數的邏輯狀態。 如果條件為真,則邏輯非運算符將為false。

          !(A and B)結果為true

          其它運算符

          編號

          描述

          示例

          ..

          連接兩個字符串

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

          #

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

          #"Hello" 將返回 5

          Lua運算符優先級

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

          類別

          操作符

          關聯性

          一元

          not # -

          右到左

          連接

          ..

          右到左

          乘法

          * / %

          左到右

          加法

          + -

          左到右

          關系

          < > <=>===~=

          左到右

          相等

          ==~=

          左到右

          邏輯與

          and

          左到右

          邏輯或

          or

          左到右

          Lua循環

          Lua提供以下類型的循環來處理循環需求。

          編號

          循環類型

          描述


          1

          while循環

          在給定條件為真時重復語句或語句組,它在執行循環體之前測試條件。

          while(condition)
          do
          statement(s)
          end

          2

          for循環

          多次執行一系列語句,并縮寫管理循環變量的代碼。

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

          3

          repeat…unti循環

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

          repeat
          statement(s)
          until( condition )

          4

          嵌套循環

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


          循環控制聲明

          循環控制語句從正常順序改變執行。 當執行離開作用域時,將銷毀在該作用域中創建的所有自動對象。

          Lua支持以下控制語句。

          編號

          控制語句

          描述

          1

          break語句

          終止循環并將執行轉移到循環或switch之后的語句。

          無限循環

          如果條件永遠不會變為假,則循環變為無限循環。 while循環通常用于此目的。如果直接給出了一個條件為真,它就會永遠執行。可以使用break語句來打斷這個無限循環。

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

          Lua決策結構

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

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

          編號

          決策語句

          描述

          1

          if語句

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

          2

          if…else語句

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

          3

          嵌套if語句

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

          Lua函數

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

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

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

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

          Lua字符串

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

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

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

          轉義序列

          用法

          \a

          \b

          退格

          \f

          換頁

          \n

          新行

          \r

          回車

          \t

          制表符

          \v

          垂直制表符

          \

          反斜杠

          "

          雙引號

          '

          單引號

          \[

          左方括號

          \]

          右方括號

          字符串操作

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

          編號

          方法

          作用

          1

          string.upper(argument)

          返回參數的大寫表示。

          2

          string.lower(argument)

          返回參數的小寫表示。

          3

          string.gsub(mainString,findString,replaceString)

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

          4

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

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

          5

          string.reverse(arg)

          將傳遞的字符串arg反轉后的字符串。

          6

          string.format(...)

          返回格式化字符串。

          7

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

          返回輸入參數的內部數字和字符表示。

          8

          string.len(arg)

          返回傳遞字符串arg的長度。

          9

          string.rep(string, n))

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

          10

          ..

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

          Lua數組

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

          在Lua中,使用帶整數的索引表實現數組。 數組的大小不固定,它可以根據要求增長,受內存限制。

          一維數組

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

          多維數組

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

          Lua迭代器

          迭代器泛型

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

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

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

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

          • 無狀態迭代器
          • 有狀態迭代器

          無狀態迭代器

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

          function square(iteratorMaxCount,currentNumber)
          
            if currentNumber<iteratorMaxCount
            then
              currentNumber=currentNumber+1
              return currentNumber, currentNumber*currentNumber
            end
          end
          
          for i,n in square,3,0
          do
            print(i,n)
          end

          稍微修改上面的代碼,以模仿迭代器的ipairs函數的工作方式。 如下代碼所示 -

          function square(iteratorMaxCount,currentNumber)
          
            if currentNumber<iteratorMaxCount
            then
              currentNumber=currentNumber+1
              return currentNumber, currentNumber*currentNumber
            end
          end
          
          function squares(iteratorMaxCount)
            return square,iteratorMaxCount,0
          end 
          
          for i,n in squares(3)
          do 
            print(i,n)
          end

          有狀態迭代器

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

          現在來看看一個創建迭代器的例子,并將使用閉包。

          array={"Lua", "Tutorial"}
          function elementIterator (collection)
            local index=0
            local count=#collection
          
            -- The closure function is returned
            return function ()
                  index=index + 1
                  if index <=count
                  then
                    -- return the current element of the iterator
                    return collection[index]
                  end
               end
          end
          
          for element in elementIterator(array)
          do
            print(element)
          end

          當運行上面的程序時,將得到以下輸出 -

          Lua
          Tutorial

          在上面的例子中,可以看到elementIterator中有另一個方法,它使用局部外部變量index和count來通過每次調用函數時遞增索引來返回集合中的每個元素。 可使用閉包創建函數迭代器,如上所示,它可以為迭代整個集合的每個時間返回多個元素。

          Lua表

          表是Lua中唯一可用的數據結構,使用表可以創建不同的類型,如數組和字典。 Lua使用關聯數組,不僅可使用數字編制索引,還可以使用除nil之外的字符串編制索引。 表沒有固定的大小,可以根據需要增長大小。

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

          表示和用法

          表稱為對象,它既不是值也不是變量。 Lua使用構造函數表達式{}來創建一個空表。 應該知道,保持表的引用的變量和表本身之間沒有固定的關系。

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

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

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

          -- Simple empty table
          mytable={}
          print("Type of mytable is ",type(mytable))
          mytable[1]="Lua"
          mytable["wow"]="Tutorial"
          print("mytable Element at index 1 is ", mytable[1])
          print("mytable Element at index wow is ", mytable["wow"])
          
          -- alternatetable and mytable refers to same table
          alternatetable=mytable
          print("alternatetable Element at index 1 is ", alternatetable[1])
          print("mytable Element at index wow is ", alternatetable["wow"])
          alternatetable["wow"]="I changed it"
          print("mytable Element at index wow is ", mytable["wow"])
          
          -- only variable released and and not table
          alternatetable=nil
          print("alternatetable is ", alternatetable)
          
          -- mytable is still accessible
          print("mytable Element at index wow is ", mytable["wow"])
          mytable=nil
          print("mytable is ", mytable)

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

          Type of mytable is table
          mytable Element at index 1 is Lua
          mytable Element at index wow is Tutorial
          alternatetable Element at index 1 is Lua
          mytable Element at index wow is Tutorial
          mytable Element at index wow is I changed it
          alternatetable is nil
          mytable Element at index wow is I changed it
          mytable is nil

          表操作

          下面是用于表操作的內置函數,它們列在下表格中。

          編號

          方法

          作用

          1

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

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

          2

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

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

          3

          table.maxn(table)

          返回最大的數字索引。

          4

          table.remove(table [, pos])

          從表中刪除值。

          5

          table.sort(table [, comp])

          根據可選的比較器參數對表進行排序。

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

          1. 表連接

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

          fruits={"banana","orange","apple"}
          
          -- returns concatenated string of table
          print("Concatenated string ",table.concat(fruits))
          
          --concatenate with a character
          print("Concatenated string ",table.concat(fruits,", "))
          
          --concatenate fruits based on index
          print("Concatenated string ",table.concat(fruits,", ", 2,3))

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

          Concatenated string bananaorangeapple
          Concatenated string banana, orange, apple
          Concatenated string orange, apple

          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. 排序表

          有時想要按特定順序對表進行排序。 排序函數按字母順序對表中的元素進行排序。 這方面的示例如下所示。

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

          Lua模塊的特色

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

          -- Assuming we have a module printFormatter
          -- Also printFormatter has a funtion simpleFormat(arg)
          -- Method 1
          require "printFormatter"
          printFormatter.simpleFormat("test")
          -- Method 2
          local formatter=require "printFormatter"
          formatter.simpleFormat("test")
          -- Method 3
          require "printFormatter"
          local formatterFunction=printFormatter.simpleFormat
          formatterFunction("test")

          在上面的示例代碼中,可以看到Lua中的編程靈活性,沒有任何特殊的附加代碼。

          require函數

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

          示例

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

          local mymath={}
          
          function mymath.add(a,b)
            print(a+b)
          end
          
          function mymath.sub(a,b)
            print(a-b)
          end
          
          function mymath.mul(a,b)
            print(a*b)
          end
          
          function mymath.div(a,b)
            print(a/b)
          end
          
          return mymath

          現在,為了在另一個文件(例如,moduletutorial.lua)中訪問此Lua模塊,需要使用以下代碼段。

          package.path='/Users/fei/work/lua_work/learn/mymath.lua'
          mymathmodule=require("mymath")
          mymathmodule.add(10,20)
          mymathmodule.sub(30,20)
          mymathmodule.mul(10,20)
          mymathmodule.div(30,20)

          要運行此代碼,需要將兩個Lua文件放在同一目錄中,或者,可以將模塊文件放在包路徑(package.path)中,它需要額外的設置。 當運行上面的程序時,將得到以下輸出 -

          30
          10
          200
          1.5

          注意事項

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

          實現模塊的舊方法

          下面將以舊方式重寫上面相同的示例,它使用package.seeall類型的實現。 這在Lua版本5.1和5.0中使用。 mymath模塊如下所示。

          module("mymath", package.seeall)
          
          function mymath.add(a,b)
            print(a+b)
          end
          
          function mymath.sub(a,b)
            print(a-b)
          end
          
          function mymath.mul(a,b)
            print(a*b)
          end
          
          function mymath.div(a,b)
            print(a/b)
          end

          moduletutorial.lua 中模塊的用法如下所示:

          require("mymath")
          mymath.add(10,20)
          mymath.sub(30,20)
          mymath.mul(10,20)
          mymath.div(30,20)

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

          Lua元表

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

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

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

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

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

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

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

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

          部分元方法介紹

          __index     -- 'table[key]',取下標操作,用于訪問表中的域
          __newindex  -- 'table[key]=value',賦值操作,增改表中的域
          __call      -- 'func(args)',函數調用,(參見 《Lua 學習筆記(三)—— 表達式》中的函數部分介紹)
          
          -- 數學運算操作符
          __add -- '+'
          __sub -- '-'
          __mul -- '*'
          __div -- '/'
          __mod -- '%'
          __pow -- '^'
          __unm -- '-'
          
          -- 連接操作符
          __concat -- '..'
          
          -- 取長操作符
          __len -- '#'
          
          -- 比較操作符
          __eq -- '=='
          __lt -- '<' -- a > b 等價于 b < a
          __le -- '<=' -- a >=b 等價于 b <=a 
          
          ## 參考: https://blog.csdn.net/yangzhenzhen/article/details/51233548

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

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

          value1 metatablevalue

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

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

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

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

          __newindex

          當將__newindex添加到metatable時,如果表中沒有鍵,則新鍵的行為將由元方法定義。 下面給出了當主表中沒有索引時設置metatable索引的簡單示例。

          mymetatable={}
          mytable=setmetatable({key1="value1"}, { __newindex=mymetatable })
          
          print(mytable.key1)
          
          mytable.newkey="new value 2"
          print(mytable.newkey,mymetatable.newkey)
          
          mytable.key1="new value 1"
          print(mytable.key1,mymetatable.newkey1)

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

          value1
          nil new value 2
          new value 1 nil

          在上面的程序中看到,如果主表中存在一個鍵,它只會更新它。 當維護中的鍵不可用時,它會將該鍵添加到metatable中。

          使用rawset函數更新同一個表的另一個示例如下所示 -

          mytable=setmetatable({key1="value1"}, {
            __newindex=function(mytable, key, value)
              rawset(mytable, key, "\""..value.."\"")
          end
          })
          
          mytable.key1="new value"
          mytable.key2=4
          
          print(mytable.key1,mytable.key2)

          當運行上面的程序時,將獲得以下輸出。

          new value "4"

          rawset設置值而不使用元表的__newindex。 類似地,有一個rawget可以在不使用__index的情況下獲取值。

          向表中添加運算符行為

          使用+運算符組合兩個表的簡單示例如下所示 -

          mytable=setmetatable({ 1, 2, 3 }, {
            __add=function(mytable, newtable)
          
              for i=1, (#newtable) do
                table.insert(mytable, (#mytable)+1,newtable[i])
              end
              return mytable
            end
          })
          
          secondtable={4,5,6}
          mytable=mytable + secondtable
          print(table.concat(mytable," , "))
          for k,v in ipairs(mytable) do
            print(k,v)
          end

          當運行上面的程序時,將得到以下輸出 -

          1 , 2 , 3 , 4 , 5 , 6
          1 1
          2 2
          3 3
          4 4
          5 5
          6 6

          __add鍵包含在元表中以添加運算符 + 的行為。鍵表和相應的操作符如下所示。

          編號

          模式

          描述

          1

          __add

          改變運算符+的行為。

          2

          __sub

          改變運算符-的行為。

          3

          __mul

          改變運算符*的行為。

          4

          __div

          改變運算符/的行為。

          5

          __mod

          改變運算符%的行為。

          6

          __unm

          改變運算符-的行為。

          7

          __concat

          改變運算符..的行為。

          8

          __eq

          改變運算符==的行為。

          9

          __lt

          改變運算符<的行為。

          10

          __le

          改變運算符<=的行為。

          __call

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

          mytable=setmetatable({10}, {
            __call=function(mytable, newtable)
              sum=0
          
              for i=1, (#mytable) do
                sum=sum + mytable[i]
              end
              
              for i=1, (#newtable) do
                sum=sum + newtable[i]
              end
          
              return sum
            end
          })
          
          newtable={10,20,30}
          print(mytable(newtable))
          

          當運行上面的程序時,將得到以下輸出。

          70

          __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

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

          Lua協同程序 ??

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

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

          協同程序函數

          下表列出了Lua中協程的所有可用函數及其相應的用法。

          編號

          方法

          作用或目的

          1

          coroutine.create (f)

          使用函數f創建一個新的協同程序,并返回thread類型的對象。

          2

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

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

          3

          coroutine.running ()

          如果在主線程中調用,則返回正在運行的協同程序或nil。

          4

          coroutine.status (co)

          根據協同程序的狀態返回running,normal,suspended或dead中的一個值。

          5

          coroutine.wrap (f)

          與coroutine.create一樣,coroutine.wrap函數也會創建一個協同程序,但它不會返回協同程序本身,而是返回一個函數,當調用它時,它會恢復協同程序。

          6

          coroutine.yield (...)

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

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

          co=coroutine.create(function (value1,value2)
            local tempvar3=10
            print("coroutine section 1", value1, value2, tempvar3)
          
            local tempvar1=coroutine.yield(value1+1,value2+1)
            tempvar3=tempvar3 + value1
            print("coroutine section 2",tempvar1 ,tempvar2, tempvar3)
          
            local tempvar1, tempvar2=coroutine.yield(value1+value2, value1-value2)
            tempvar3=tempvar3 + value1
            print("coroutine section 3",tempvar1,tempvar2, tempvar3)
            return value2, "end"
          end)
          
          print("main", coroutine.resume(co, 3, 2))
          print("main", coroutine.resume(co, 12,14))
          print("main", coroutine.resume(co, 5, 6))
          print("main", coroutine.resume(co, 10, 20))

          當運行上面的程序時,將得到以下輸出 -

          coroutine section 1 3 2 10
          main true 4 3
          coroutine section 2 12 nil 13
          main true 5 1
          coroutine section 3 5 6 16
          main true 2 end
          main false cannot resume dead coroutine
          Shell

          上面的例子是實現什么功能?

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

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

          另一個協同程序示例

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

          function getNumber()
              local function getNumberHelper()
                  co=coroutine.create(function ()
                  coroutine.yield(1)
                  coroutine.yield(2)
                  coroutine.yield(3)
                  coroutine.yield(4)
                  coroutine.yield(5)
                  end)
                  return co
              end
              
            if(numberHelper) then
                status, number=coroutine.resume(numberHelper);
                    if coroutine.status(numberHelper)=="dead" then
                      numberHelper=getNumberHelper()
                      status, number=coroutine.resume(numberHelper);
                    end
          
                    return number
                else
                    numberHelper=getNumberHelper()
                    status, number=coroutine.resume(numberHelper);
                    return number
            end
          end
          
          
          for index=1, 10 do
          	print(index, getNumber())
          end

          當運行上面的程序時,將得到以下輸出。

          1 1
          2 2
          3 3
          4 4
          5 5
          6 1
          7 2
          8 3
          9 4
          10 5

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

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

          Lua文件操作


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

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

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

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

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

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

          編號

          模式

          描述

          1

          r

          只讀模式,是打開現有文件的默認模式。

          2

          w

          寫入啟用模式,覆蓋現有文件或創建新文件。

          3

          a

          附加模式,用于打開現有文件或創建新文件以進行追加。

          4

          r+

          現有文件的讀寫模式。

          5

          w+

          如果文件存在或創建具有讀寫權限的新文件,則刪除所有現有數據。

          6

          a+

          啟用了讀取模式的追加模式可打開現有文件或創建新文件。

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

          -- Sample test.lua

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

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

          編號

          模式

          描述

          1

          *n

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

          2

          *a

          從當前文件位置返回文件的所有內容。

          3

          *l

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

          4

          number

          讀取函數中指定的字節數。

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

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

          2. 顯式文件描述符

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

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

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

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

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

          -- Opens a file in read
          file=io.open("test.lua", "r")
          file:seek("end",-25)
          print(file:read("*a"))
          
          -- closes the opened file
          file:close()

          執行上面示例代碼,將獲得類似于以下內容的輸出 -

          sample2 test.lua
          --test

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

          Lua錯誤處理

          錯誤處理非常關鍵,因為實際操作通常需要使用復雜的操作,包括文件操作,數據庫事務和Web服務調用。

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

          • 語法錯誤
          • 運行時錯誤

          1. 語法錯誤

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

          a==2

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

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

          for a=1,10
            print(a)
          end

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

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

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

          2. 運行時錯誤

          如果出現運行時錯誤,程序將成功執行,但由于輸入錯誤或錯誤處理的函數,可能會導致運行時錯誤。 顯示運行時錯誤的簡單示例如下所示。

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

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

          lua: test2.lua:2: attempt to perform arithmetic on local 'b' (a nil value)
          stack traceback:
          test2.lua:2: in function 'add'
          test2.lua:5: in main chunk
          [C]: ?

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

          斷言和錯誤功能

          要處理錯誤,經常使用兩個函數是 - assert和error。 一個簡單的例子如下所示。

          local function add(a,b)
            assert(type(a)=="number", "a is not a number")
            assert(type(b)=="number", "b is not a number")
            return a+b
          end
          
          add(10)

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

          lua: test2.lua:3: b is not a number
          stack traceback:
          [C]: in function 'assert'
          test2.lua:3: in function 'add'
          test2.lua:6: in main chunk
          [C]: ?
          error(message [,level])終止最后一個調用的受保護函數,并將消息作為錯誤消息返回。 此函數錯誤永遠不會返回。 通常,錯誤會在消息開頭添加有關錯誤位置的一些信息。 level參數指定如何獲取錯誤位置。 對于級別1(默認值),錯誤位置是調用錯誤函數的位置。 級別2將錯誤指向調用調用錯誤的函數的位置等等。 傳遞0級可避免向消息添加錯誤位置信息。

          pcall和xpcall

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

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

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

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

          Failure

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

          xpcall函數的一個簡單示例如下所示 -

          function myfunction ()
            n=n/nil
          end
          
          function myerrorhandler( err )
            print( "ERROR:", err )
          end
          
          status=xpcall( myfunction, myerrorhandler )
          print( status)

          當運行上面的程序時,將得到以下輸出。

          ERROR: test2.lua:2: attempt to perform arithmetic on global 'n' (a nil value)
          false

          作為程序員,最重要的是確保在編寫的程序中處理正確的錯誤處理。 使用錯誤處理可以確保處理超出邊界條件的意外條件而不會干擾程序的用戶。

          Lua調試 ??

          Lua提供了一個調試庫,它提供了創建自己的調試器的原始函數。 即使沒有內置的Lua調試器,也有許多第三方Lua調試器,由各種開發人員創建,其中許多是開源的。

          下表中列出了Lua調試庫中可用的功能及其用法。

          編號

          方法

          描述

          1

          debug()

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

          2

          getfenv(object)

          返回對象的環境。

          3

          gethook(optional thread)

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

          4

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

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

          5

          getlocal(optional thread, stack level, local index)


          6

          getmetatable(value)

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

          7

          getregistry()

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

          8

          getupvalue(function, upvalue index)

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

          9

          setfenv(function or thread or userdata, environment table)

          將給定對象的環境設置為給定表,返回對象。

          10

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

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

          11

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

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

          12

          setmetatable(value, metatable)

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

          13

          setupvalue(function, upvalue index, value)

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

          14

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

          使用回溯構建擴展錯誤消息。

          上面的列表是Lua中完整的調試函數列表,經常使用一個上述函數的庫,并提供更簡單的調試。 使用這些函數并創建我們自己的調試器非常復雜,并不是優選的。 下面來看一個簡單使用調試功能的例子。

          function myfunction ()
              print(debug.traceback("Stack trace"))
              print(debug.getinfo(1))
              print("Stack trace end")
              return 10
          end
          
          myfunction ()
          print(debug.getinfo(1))

          當運行上面的程序時,將獲得如下所示的堆棧跟蹤 -

          Stack trace
          stack traceback:
          test2.lua:2: in function 'myfunction'
          test2.lua:8: in main chunk
          [C]: ?
          table: 0054C6C8
          Stack trace end

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

          調試示例

          開發人員經常為了知道函數的局部變量以進行調試。 為此,可以使用getupvalue和使用setupvalue并設置這些局部變量。 一個簡單的例子如下所示 -

          function newCounter ()
              local n=0
              local k=0
          
              return function ()
                  k=n
                  n=n + 1
                  return n
              end
          end
          
          counter=newCounter ()
          print(counter())
          print(counter())
          
          local i=1
          repeat
          		name, val=debug.getupvalue(counter, i)
          
              if name then
                  print ("index", i, name, "=", val)
          
                  if(name=="n") then
                      debug.setupvalue (counter,2,10)
                  end
                  i=i + 1
              end -- if
          until not name
          print(counter())

          當運行上面的程序時,將獲得如下所示的堆棧跟蹤 -

          1
          2
          index 1 k=1
          index 2 n=2
          11

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

          調試類型

          在Lua中,調試類型主要有兩種 -

          • 命令行調試
          • 圖形化調試

          1. 命令行調試

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

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

          2. 圖形調試

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

          Lua有許多圖形調試器,它包括以下內容-

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

          Lua垃圾收集

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

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

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

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

          垃圾收集器暫停

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

          垃圾收集器步驟乘數

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

          垃圾收集器函數

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

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

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

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

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

          22.599609375
          20.79296875
          0
          21.5537109375

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

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

          Lua面向對象編程

          面向對象編程(OOP)是現代編程中使用最多的編程技術之一。 有許多支持OOP的編程語言包括 -

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

          面向對象的特點

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

          Lua面向對象

          可使用Lua的表和第一類函數在Lua中實現面向對象。 通過將函數和相關數據放入表中,形成對象??墒褂迷韺崿F繼承,為父對象中不存在的函數(方法)和字段提供查找機制。

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

          真實世界的例子

          面向對象的概念廣泛使用,但需要清楚地理解面向對象以獲得適當和最大的好處。 考慮一個簡單的數學例子。 我們經常遇到處理不同形狀的情況,如圓形,矩形和方形。

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

          創建一個簡單的類

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

          -- Meta class
          Rectangle={area=0, length=0, breadth=0}
          
          -- Derived class method new
          function Rectangle:new (o,length,breadth)
              o=o or {}
              setmetatable(o, self)
              self.__index=self
              self.length=length or 0
              self.breadth=breadth or 0
              self.area=length*breadth;
              return o
          end
          
          -- Derived class method printArea
          function Rectangle:printArea ()
              print("The area of Rectangle is ",self.area)
          end

          創建一個對象 創建對象是為類實例分配內存的過程。 每個對象都有自己的內存并共享公共類數據。

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

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

          print(r.length)

          訪問成員函數

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

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

          分配內存并設置初始值??梢詫⒊跏蓟^程與其他面向對象語言中的構造函數進行比較。 它只是一個能夠設置如上所示的值的函數。

          完整的例子 下面來看一下在Lua中使用面向對象的完整示例。代碼如下 -

          -- Meta class
          Shape={area=0}
          
          -- Base class method new
          function Shape:new (o,side)
              o=o or {}
              setmetatable(o, self)
              self.__index=self
              side=side or 0
              self.area=side*side;
              return o
          end
          
          -- Base class method printArea
          function Shape:printArea ()
          		print("The area is ",self.area)
          end
          
          -- Creating an object
          myshape=Shape:new(nil,10)
          myshape:printArea()

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

          The area is 100

          Lua繼承

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

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

          -- 元類
          Shape={area=0}
          
          -- 基類方法
          function Shape:new (o,side)
          o=o or {}
              setmetatable(o, self)
              self.__index=self
              side=side or 0
              self.area=side*side;
              return o
          end
          
          -- 基類方法 - printArea
          function Shape:printArea ()
          		print("The area is ",self.area)
          end
          

          可以將形狀擴展為方形類,如下所示。

          Square=Shape:new()
          
          -- Derived class method new
          function Square:new(o,side)
              o=o or Shape:new(o,side)
              setmetatable(o, self)
              self.__index=self
              return o
          end

          覆蓋基類函數

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

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

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

          -- Meta class
          Shape={area=0}
          
          -- Base class method new
          function Shape:new (o,side)
              o=o or {}
              setmetatable(o, self)
              self.__index=self
              side=side or 0
              self.area=side*side;
              return o
          end
          
          -- Base class method printArea
          function Shape:printArea ()
          		print("The area is ",self.area)
          end
          
          -- Creating an object
          myshape=Shape:new(nil,10)
          myshape:printArea()
          Square=Shape:new()
          
          -- Derived class method new
          function Square:new (o,side)
              o=o or Shape:new(o,side)
              setmetatable(o, self)
              self.__index=self
              return o
          end
          
          -- Derived class method printArea
          function Square:printArea ()
          		print("The area of square is ",self.area)
          end
          
          -- Creating an object
          mysquare=Square:new(nil,10)
          mysquare:printArea()
          Rectangle=Shape:new()
          
          -- Derived class method new
          function Rectangle:new (o,length,breadth)
              o=o or Shape:new(o)
              setmetatable(o, self)
              self.__index=self
              self.area=length * breadth
              return o
          end
          
          -- Derived class method printArea
          function Rectangle:printArea ()
          		print("The area of Rectangle is ",self.area)
          end
          
          -- Creating an object
          myrectangle=Rectangle:new(nil,10,20)
          myrectangle:printArea()

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

          The area is 100
          The area of square is 100
          The area of Rectangle is 200

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

          Lua Web編程

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

          盡管已經有很多Lua Web開發是使用Lua的Web框架,但這里主要介紹和使用Kepler社區提供的組件。

          Lua常用應用程序和框架

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

          在本教程中,將讓您了解Lua可以執行的操作,并了解有關其安裝和使用的更多信息,請參閱kepler網站。

          Orbit

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

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

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

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

          安裝Apache 連接到服務器,使用以下命令安裝Apache2及其支持模塊并啟用所需的Apache2模塊 -

          $ sudo apt-get install apache2 libapache2-mod-fcgid libfcgi-dev build-essential
          $ sudo a2enmod rewrite
          $ sudo a2enmod fcgid
          $ sudo /etc/init.d/apache2 force-reload

          安裝LuaRocks

          $ sudo apt-get install luarocks

          安裝WSAPI,FCGI,Orbit和Xavante

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

          設置Apache2

          $ sudo raj /etc/apache2/sites-available/default

          在配置文件的<Directory /var/www/>部分下面添加以下部分。 如果此部分有AllowOverride None,則需要將None更改為All,以便.htaccess文件可以在本地覆蓋配置。

          <IfModule mod_fcgid.c>
              AddHandler fcgid-script .lua
              AddHandler fcgid-script .ws
              AddHandler fcgid-script .op
          
              FCGIWrapper "/usr/local/bin/wsapi.fcgi" .ws
              FCGIWrapper "/usr/local/bin/wsapi.fcgi" .lua
              FCGIWrapper "/usr/local/bin/op.fcgi" .op
          
              #FCGIServer "/usr/local/bin/wsapi.fcgi" -idle-timeout 60 -processes 1
              #IdleTimeout 60
              #ProcessLifeTime 60
          
          </IfModule>

          重新啟動服務器以確保所做的更改生效。 要啟用應用程序,需要將+ExecCGI添加到Orbit應用程序根目錄中的.htaccess文件中 - 在本例中目錄為/var/www。

          Options +ExecCGI
          DirectoryIndex index.ws

          Orbit簡單示例 -

          #!/usr/bin/env index.lua
          
          -- index.lua
          require"orbit"
          
          -- declaration
          module("myorbit", package.seeall, orbit.new)
          
          -- handler
          function index(web)
          	return my_home_page()
          end
          
          -- dispatch
          myorbit:dispatch_get(index, "/", "/index")
          
          -- Sample page
          function my_home_page()
          
          end

          現在,可以啟動Web瀏覽器了。 轉到http://localhost:8080/,應該看到以下輸出 -

          First Page

          Orbit提供了另一種選擇,即Lua代碼可以生成html。

          #!/usr/bin/env index.lua
          -- index.lua
          require"orbit"
          
          function generate()
          	return html {
          			head{title "HTML Example"},
          			body{
          					h2{"Here we go again!"}
          			}
          	}
          end
          
          orbit.htmlify(generate)
          print(generate())

          創建表單

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

          #!/usr/bin/env index.lua
          require"orbit"
          function wrap (inner)
          		return html{ head(), body(inner) }
          end
          
          function test ()
              return wrap(form (H'table' {
                  tr{td"First name",td( input{type='text', name='first'})},
                  tr{td"Second name",td(input{type='text', name='second'})},
                  tr{ td(input{type='submit', value='Submit!'}),
                  		td(input{type='submit',value='Cancel'})
                  },
              }))
          end
          
          orbit.htmlify(wrap,test)
          print(test())

          WSAPI

          如前所述,WSAPI充當許多項目的基礎,并在其中嵌入了多個功能。 可以使用WSAPI并支持以下平臺 -

          • Windows
          • UNIX類系統

          WSAPI支持的服務器和接口包括 -

          • CGI
          • FastCGI
          • Xavante

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

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

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

          #!/usr/bin/env wsapi.cgi
          
          module(..., package.seeall)
          function run(wsapi_env)
              local headers={ ["Content-type"]="text/html" }
          
              local function hello_text()
                  coroutine.yield("<html><body>")
                  coroutine.yield("<p>Hello Wsapi - by yiibai.com !</p>")
                  coroutine.yield("<p>PATH_INFO: " .. wsapi_env.PATH_INFO .. "</p>")
                  coroutine.yield("<p>SCRIPT_NAME: " .. wsapi_env.SCRIPT_NAME .. "</p>")
                  coroutine.yield("</body></html>")
          		end
              
          		return 200, headers, coroutine.wrap(hello_text)
          end

          在上面的代碼中看到形成并返回一個簡單的html 頁面??梢钥吹絽f同程序的用法,它可以將語句返回到調用函數。 最后,返回html狀態代碼(200),標題和html頁面。

          Xavante

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

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

          文件處理程序用于常規文件。重定向處理程序啟用URI重映射和WSAPI處理程序,以便與WSAPI應用程序一起處理。

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

          require "xavante.filehandler"
          require "xavante.cgiluahandler"
          require "xavante.redirecthandler"
          
          -- Define here where Xavante HTTP documents scripts are located
          local webDir=XAVANTE_WEB
          local simplerules={
            
              { -- URI remapping example
              match="^[^%./]*/$",
              with=xavante.redirecthandler,
              params={"index.lp"}
              }, 
                
              { -- cgiluahandler example
              match={"%.lp$", "%.lp/.*$", "%.lua$", "%.lua/.*$" },
              with=xavante.cgiluahandler.makeHandler (webDir)
              },
                
              { -- filehandler example
              match=".",
              with=xavante.filehandler,
              params={baseDir=webDir}
              },
                
          } 
          
          xavante.HTTP{
          		server={host="*", port=8080},
          
                defaultHost={
          					rules=simplerules
          			},
          }

          要使用Xavante的虛擬主機,對xavante.HTTP的調用將更改為如下所示 -

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

          Lua的Web組件

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

          結束說明

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

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

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

          Lua MySQL操作

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

          • SQLite
          • Mysql
          • ODBC

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

          MySQL數據庫安裝設置

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

          • 安裝并設置MySQL使用默認用戶:root,密碼為:123456。
          • 創建數據庫:test
          • 可通過學習MySQL教程來了解MySQL基礎知識。

          導入MySQL

          假設Lua實現正確完成,使用一個簡單的require語句來導入sqlite庫。

          mysql=require "luasql.mysql"

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

          設置連接

          通過啟動MySQL環境,然后為環境創建連接。 如下所示 -

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

          上述連接將連接到現有的MySQL文件,并與新創建的文件建立連接。

          執行函數

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

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

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

          創建表示例

          下面顯示了一個簡單的create table示例。 它創建一個表,其中包含兩個字段參數: id類型為integer,name類型為varchar。

          mysql=require "luasql.mysql"
          
          local env=mysql.mysql()
          local conn=env:connect('test','root','123456')
          
          print(env,conn)
          
          status,errorString=conn:execute([[CREATE TABLE sample2 (id INTEGER, name TEXT);]])
          print(status,errorString )

          運行上述程序時,將創建一個名為sample的表,其中包含兩列,即id和name。

          MySQL environment (004BB178) MySQL connection (004BE3C8)
          0 nil

          如果語句有任何錯誤,將返回錯誤語句而不是nil。 一個簡單的錯誤聲明如下所示 -

          LuaSQL: Error executing query. MySQL: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"id INTEGER, name TEXT)' at line 1

          插入語句示例 MySQL的insert語句如下所示-

          conn:execute([[INSERT INTO sample values('11','Maxsu')]])

          更新語句示例 MySQL的更新語句如下所示 -

          conn:execute([[UPDATE sample3 SET name='Yiibai' where id='12']])

          刪除語句示例 MySQL的刪除語句如下所示 -

          conn:execute([[DELETE from sample3 where id='12']])

          選擇語句示例 就select語句而言,我們需要循環遍歷每一行并提取所需的數據。一個簡單的選擇語句如下所示 -

          cursor,errorString=conn:execute([[select * from sample]])
          row=cursor:fetch ({}, "a")
          
          while row do
              print(string.format("Id: %s, Name: %s", row.id, row.name))
              -- reusing the table of results
              row=cursor:fetch (row, "a")
          end

          在上面的代碼中,conn是一個打開的MySQL連接。 在execute語句返回的游標的幫助下,可以遍歷表響應并獲取所需的選擇數據。

          一個完整的例子

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

          mysql=require "luasql.mysql"
          
          local env=mysql.mysql()
          local conn=env:connect('test','root','123456')
          print(env,conn)
          
          status,errorString=conn:execute([[CREATE TABLE sample3 (id INTEGER, name TEXT)]])
          print(status,errorString )
          
          status,errorString=conn:execute([[INSERT INTO sample3 values('12','Maxsu')]])
          print(status,errorString )
          
          cursor,errorString=conn:execute([[select * from sample3]])
          print(cursor,errorString)
          
          row=cursor:fetch ({}, "a")
          
          while row do
              print(string.format("Id: %s, Name: %s", row.id, row.name))
              row=cursor:fetch (row, "a")
          end
          
          -- close everything
          cursor:close()
          conn:close()
          env:close()

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

          MySQL environment (0037B178) MySQL connection (0037EBA8)
          0 nil
          1 nil
          MySQL cursor (003778A8) nil
          Id: 12, Name: Maxsu

          執行事務

          事務是一種確保數據一致性的機制。事務具有以下四個屬性 -

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

          事務以START TRANSACTION開始; 并以commit或rollback語句結束。

          開始事務

          要啟動事務,需要在Lua中執行以下語句,假設conn是一個打開的MySQL連接。

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

          回滾事務

          需要執行以下語句來回滾執行啟動事務后所做的更改。

          conn:execute([[ROLLBACK;]])

          提交事務

          需要執行以下語句來提交啟動事務執行后所做的更改。

          conn:execute([[COMMIT;]])

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

          Lua游戲編程

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

          • 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和網絡API
          • 游戲網絡API
          • 廣告API
          • 分析API
          • 數據庫和文件系統API
          • 加密和數學API
          • 音頻和媒體API

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

          2. Gideros Mobile

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

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

          3. ShiVa3D

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

          一些主要功能包括 -

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

          Shiva3d的網絡版是完全免費的,其他版本可能需要收費。

          4. Moai SDK

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

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

          5. LOVE

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

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

          • 音頻API
          • 文件系統API
          • 鍵盤和操縱桿API
          • 數學API
          • 窗口和鼠標API
          • 物理API
          • 系統和計時器API

          6. CryEngine

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

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

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

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

          Lua標準庫

          Lua標準庫提供了一組豐富的函數,這些函數直接使用C語言API實現,并使用Lua編程語言構建。這些庫提供Lua編程語言中的服務以及文件和數據庫操作之外的服務。

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

          • 基本庫,包括協程子庫
          • 模塊庫
          • 字符串操作
          • 表操作
          • 數學庫
          • 文件輸入和輸出
          • 操作系統設施
          • 調試工具

          1. 基礎庫

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

          編號

          庫/方法

          作用

          1

          錯誤處理

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

          2

          內存管理

          包括與垃圾收集相關的自動內存管理功能, 如Lua垃圾收集中所述。

          3

          dofile ([filename])

          它打開文件并以塊的形式執行文件的內容。

          4

          _G

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

          5

          getfenv ([f])

          返回函數使用的當前環境。

          6

          getmetatable (object)

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

          7

          ipairs (t)

          此函數獲取表的索引和值。

          8

          load (func [, chunkname])

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

          9

          loadfile ([filename]))

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

          10

          loadstring (string [, chunkname])

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

          11

          next (table [, index])

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

          12

          pairs (t)

          暫停正在運行的協同程序。

          13

          print (...)

          打印給定的參數值。

          14

          rawequal (v1, v2)

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

          15

          rawget (table, index)

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

          16

          rawset (table, index, value)

          將table [index]的值設置為value,而不調用任何方法。

          17

          select (index, ...)

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

          18

          setfenv (f, table)

          設置給定函數使用的環境。

          19

          setmetatable (table, metatable)

          設置給定表的元表。

          20

          tonumber (e [, base])

          嘗試將參數轉換為數字。

          21

          tostring (e)

          接收任何類型的參數并將其轉換為合理格式的字符串。

          22

          type (v)

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

          23

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

          返回給定表中的元素。

          24

          _VERSION

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

          25

          協同程序

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

          2. 模塊庫

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

          3. 字符串操作

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

          4. 表操作

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

          5. 文件輸入和輸出

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

          6. 調試工具

          Lua提供了一個調試庫,它提供了創建自己的調試器的所有原始函數。 在前面的Lua調試教程中討論過了。

          Lua數學庫

          經常需要在科學和工程計算中進行數學運算,可以使用標準的Lua庫數學來實現。 數學庫中可用的函數列表如下表所示 -

          編號

          庫或方法

          描述

          1

          math.abs(x)

          返回x的絕對值。

          2

          math.acos(x)

          返回x的弧余弦值(以弧度表示)。

          3

          math.asin(x)

          返回x的弧正弦(以弧度表示)。

          4

          math.atan(x)

          返回x的反正切(以弧度表示)。

          5

          math.atan2(y,x)

          返回y / x的反正切(以弧度表示),但使用兩個參數的符號來查找結果的象限(它也正確處理x為零的情況。)

          6

          math.ceil(x)

          返回大于或等于x的最小整數。

          7

          math.cos(x)

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

          8

          math.cosh(x)

          返回x的雙曲余弦值。

          9

          math.deg(x)

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

          10

          math.exp(x)

          返回值e的x次冪。

          11

          math.floor(x)

          返回小于或等于x的最大整數。

          12

          math.fmod(x,y)

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

          13

          math.frexp(x)

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

          14

          math.huge

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

          15

          math.ldexp(m, e)

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

          16

          math.log(x)

          返回x的自然對數。

          17

          math.log10(x)

          返回x的以10為底的對數。

          18

          math.max(x,...)

          返回參數中的最大值。

          19

          math.min(x,...)

          返回參數中的最小值。

          20

          math.modf(x)

          返回兩個數字,x的整數部分和x的小數部分。

          21

          math.pi

          pi的值。

          22

          math.pow(x,y)

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

          23

          math.rad(x)

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

          24

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

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

          25

          math.randomseed(x)

          將x設置為偽隨機生成器的“種子”:相等的種子產生相等的數字序列。

          26

          math.sin(x)

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

          27

          math.sinh(x)

          返回x的雙曲正弦值。

          28

          math.sqrt(x)

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

          29

          math.tan(x)

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

          30

          math.tanh(x)

          返回x的雙曲正切值。

          三角函數

          使用三角函數的簡單示例如下所示-

          radianVal=math.rad(math.pi / 2)
          io.write(radianVal,"\n")
          
          -- Sin value of 90(math.pi / 2) degrees
          io.write(string.format("%.1f ", math.sin(radianVal)),"\n")
          
          -- Cos value of 90(math.pi / 2) degrees
          io.write(string.format("%.1f ", math.cos(radianVal)),"\n")
          
          -- Tan value of 90(math.pi / 2) degrees
          io.write(string.format("%.1f ", math.tan(radianVal)),"\n")
          
          -- Cosh value of 90(math.pi / 2) degrees
          io.write(string.format("%.1f ", math.cosh(radianVal)),"\n")
          
          -- Pi Value in degrees
          io.write(math.deg(math.pi),"\n")

          當運行上面的程序時,將得到以下輸出 -

          0.027415567780804
          0.0 
          1.0 
          0.0 
          1.0 
          180

          其他常見的數學函數

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

          -- Floor
          io.write("Floor of 10.5055 is ", math.floor(10.5055),"\n")
          -- Ceil
          io.write("Ceil of 10.5055 is ", math.ceil(10.5055),"\n")
          -- Square root
          io.write("Square root of 16 is ",math.sqrt(16),"\n")
          -- Power
          io.write("10 power 2 is ",math.pow(10,2),"\n")
          io.write("100 power 0.5 is ",math.pow(100,0.5),"\n")
          -- Absolute
          io.write("Absolute value of -10 is ",math.abs(-10),"\n")
          --Random
          math.randomseed(os.time())
          io.write("Random number between 1 and 100 is ",math.random(),"\n")
          --Random between 1 to 100
          io.write("Random number between 1 and 100 is ",math.random(1,100),"\n")
          --Max
          io.write("Maximum in the input array is ",math.max(1,100,101,99,999),"\n")
          --Min
          io.write("Minimum in the input array is ",math.min(1,100,101,99,999),"\n")

          當運行上面的程序時,將得到以下輸出 -

          Floor of 10.5055 is 10
          Ceil of 10.5055 is 11
          Square root of 16 is 4
          10 power 2 is 100
          100 power 0.5 is 10
          Absolute value of -10 is 10
          Random number between 1 and 100 is 0.22876674703207
          Random number between 1 and 100 is 7
          Maximum in the input array is 999
          Minimum in the input array is 1

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

          Lua操作系統工具

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

          編號

          庫或方法

          描述

          1

          os.clock()

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

          2

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

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

          3

          os.difftime(t2,t1)

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

          4

          os.execute([command])

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

          5

          os.exit([code[, close])

          調用ANSI C函數出口以終止宿主程序。 如果code為true,則返回狀態為EXIT_SUCCESS; 如果code為false,則返回狀態為EXIT_FAILURE; 如果code是數字,則返回的狀態是此數字。

          6

          os.getenv(varname)

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

          7

          os.remove(filename)

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

          8

          os.rename(oldname, newname)

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

          9

          os.setlocale(locale [,category])

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

          10

          os.time([table])

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

          11

          os.tmpname()

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

          常見的OS功能

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

          -- Date with format
          io.write("The date is ", os.date("%m/%d/%Y"),"\n")
          -- Date and time
          io.write("The date and time is ", os.date(),"\n")
          -- Time
          io.write("The OS time is ", os.time(),"\n")
          -- Wait for some time
          for i=1,1000000 do
          end
          -- Time since Lua started
          io.write("Lua started before ", os.clock(),"\n")

          當運行上面的程序時,將得到類似的輸出如下 -

          The date is 01/25/2018
          The date and time is 01/25/18 07:38:40
          The OS time is 1490615720
          Lua started before 0.013

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

          ldoc

          參考

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

          說明

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

          安裝方法

          brew install luarocks -v
          luarocks install Ldoc

          使用方法

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

          一、lua編寫和調試

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

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

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

          二、lua語法

          1、Lua數據類型

          • nil

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

          • number

          • string

          • userdata

          • function:函數

          • thread

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

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

          print(type("Hello World")) --> stringprint(type(10.4*3)) --> numberprint(type(print)) --> functionprint(type(type(X))) --> string

          2、Lua table類型的使用

          table可以當做array來使用,但是不要求子元素類型一致

          local days={}--這里聲明了一個table,變量名是days,local代表局部變量
          days[1]="Sunday"--請注意,正常語言都是從0開始,lua的下標是從1開始
          days[2]="Monday"days[3]=20--注意這里,正常語言的array都要求元素的類型是一直的,lua沒這個限制
          print(days[1])--print函數是把變量打印到控制臺,如果在wow聊天框運行,就打印到聊天框
          --lua作為array使用,并不用提前指定的他的長度,這點還是挺爽的

          table可以當做map來使用,保存key/value

          local a={}a["x"]=10a["y"]=20--或者這樣寫,效果是一樣的
          local a={}
          a={
           ["x"]=10,
           ["y"]=20}print(a["x"])--使用方括號來訪問keyprint(a.x)--直接.也是可以的,這種方式比較常用,畢竟簡單啊

          甚至可以嵌套

          local inner={}
          inner[1]=10inner[2]="test"inner.zz="sleep"local outer={}outer.x=innerouter.y=2print(outer.x.zz)

          求table的size,用#號就可以了

          local a={1,2,"a"}print(#a) -->3

          3、加減乘除冪模、邏輯運算

          1)表達式:

          +(加)

          -(減)

          *(乘)

          /(除)

          ^(冪)

          %(取模)

          2)邏輯運算符

          and or not

          print(true and true)-->trueprint(false and true)--falseprint(true or false)-->trueprint(not 1)--falseprint(not 0)-->false,前面說過了,0也是真,別忘了
          --基本跟其他語言沒有任何區別,不廢話了

          4、關系運算

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

          5、局部變量和全局變量

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

          6、if/else

          if true thenelseif true thenelseend

          7、while循環

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

          8、for循環

          for i=1, 3 do
           print(i)endfor k, v in pairs(t) do
           print(k, v)endfor i, v in ipairs(t) do
           print(i, v)end

          9、repeat..until

          repeat
           line=io.read()until line ~=''print(line)

          10、goto

          goto quit
          print('come on')::quit::print('quit')
          --這里只輸出quit

          10、break、return

          break是結束循環,return是返回,lua不支持continue

          11、注釋

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

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

          12、函數

          來看一下常規的,求兩個數字中最大的

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

          lua的函數可以返回多個值

          function GetPlayerPosition(unit)
           if unit=="player" then
           return 1,2,3
           endend--返回自己的坐標local x,y,z=GetPlayerPosition("player")print(x)print(y)print(z)print(x,y,z)--print也支持傳多個參數,一次性全打印出來,中間用空格分隔

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

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

          lua的函數可以接收任意個數的參數


          主站蜘蛛池模板: 国产一区二区女内射| 色狠狠一区二区三区香蕉蜜桃 | 国产在线不卡一区| 麻豆va一区二区三区久久浪| 国产精品一区二区四区| 亚洲一区二区视频在线观看| 在线观看国产区亚洲一区成人| 久久综合精品不卡一区二区| 国产成人亚洲综合一区| 国内精自品线一区91| 一区二区三区久久精品| 少妇精品无码一区二区三区 | 国产肥熟女视频一区二区三区| 亚洲av无码天堂一区二区三区| 亚洲国产精品无码久久一区二区| 中文字幕日韩一区二区不卡| 亚洲国产一区二区视频网站| 骚片AV蜜桃精品一区| 无码人妻精品一区二区三区不卡| 国产色精品vr一区区三区| 日本激情一区二区三区| 国产情侣一区二区三区| 亚洲无圣光一区二区| jazzjazz国产精品一区二区| 日本一区频道在线视频| 国产激情一区二区三区| 亚洲熟妇无码一区二区三区导航| 国产福利电影一区二区三区久久老子无码午夜伦不 | 国产精品无码一区二区在线观| 精品国产AⅤ一区二区三区4区| 亚洲第一区二区快射影院| 爆乳熟妇一区二区三区| 在线免费观看一区二区三区| 台湾无码一区二区| 国产福利一区二区在线视频 | 国产日韩精品一区二区在线观看播放| 亚洲中文字幕无码一区| 免费萌白酱国产一区二区| 中文字幕av人妻少妇一区二区| 中文字幕aⅴ人妻一区二区| 在线一区二区三区|