<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                盡管Lua是一門解析型的語言,但是在運行前也會被編譯成某個中間狀態。一門解析型的語言需要編譯,這聽起來有點不合常理。但是,實際上,解析型語言的與眾不同,不是說它不需要編譯,而是說它把編譯作為其運行時的一部分,因此,它就可以執行各種來自外部的代碼(例如網上的)。也許因為Lua中存在的如*dofile* 這樣的函數,才使Lua可以被稱為一門解析型語言。 1. 編譯 之前我們介紹了*dofile* 來執行代碼塊,但是*dofile* 只是一個輔助函數。這里介紹一下*loadfile* 函數,它會從一個file中加載語句塊,但是不運行;而是僅僅編譯并作為一個函數返回。*loadfile* 不會像*dofile* 那樣在運行時直接報錯退出,而是返回錯誤碼,這樣我們就可以根據錯誤碼做相應的處理。我們可以像下面這樣定義*dofile* ,這也可以看出*dofile* 和*loadfile* 的區別 ~~~ function dofile (filename) local f = assert(loadfile(filename)) return f() end ~~~ 注意,*assert* 可以使*loadfile* 發生錯誤時,報錯退出。 *dofile* 在處理有些簡單的任務時,使用起來比較方便,只需要一次調用,它會完成所有的操作(編譯,運行啥的)。但是,*loadfile*更加靈活。發生錯誤的時候,*loadfile* 會返回**nil**+ 'err_msg',我們可以根據實際情況對錯誤做出相應的處理。除此之外,如果想要運行一個file多次,可以先調用一次*loadfile* ,然后調用*loadfile* 返回的結果多次,就可以了。這比調用幾次*dofile* 開銷要小很多,因為*loadfile* 只會執行一次編譯,而*dofile* 每次都用都要編譯。 *loadstring* 跟*loadfile* 差不多,區別是從一個string中加載代碼塊,而不是從file中。例如: ~~~ f = loadstring("i = i + 1") ~~~ *f* 是*loadstring* 的返回值,應該是function類型, 調用的時候,會執行i = i + 1 :? ~~~ i = 0 f(); print(i) --> 1 f(); print(i) --> 2 ~~~ *loadstring* 函數功能非常強大,但是運行起來,開銷也不小,并且有時會導致產生一些莫名其妙的代碼。因此,在用*loadstring* 之前,先考慮下有沒有更簡單的辦法。 下面這行代碼,不太好看,但是很方便。(不鼓勵這種調用方法) ~~~ loadstring(s)() ~~~ 如果有語法錯誤,*loadstring* 會返回**nil?**+ 類似‘attempt to call a nil value’這樣的err_msg。如果想獲取更詳細的err_msg,那就需要用*assert* : ~~~ assert(loadstring(s))() ~~~ 下面這樣的使用方式(對一個字面值string使用loadstring),沒什么意思, ~~~ f = loadstring("i = i + 1") ~~~ 粗略的等價于: ~~~ f = function () i = i + 1 end ~~~ 但是,也不是完全相同,且繼續往下看。第二種方式的代碼運行起來更快,因為它只需要編譯一次,而*loadstring* 每次都需要編譯。下面我們來看看,上面兩段代碼到底有什么不同,如下示例: ~~~ i = 32 local i = 0 f = loadstring("i = i + 1; print(i)") g = function () i = i + 1; print(i) end f() --> 33 g() --> 1 ~~~ *g* 函數處理的事局部變量i , 而*f* 函數處理的是全局變量i ,*loadstring* 總是在全局環境中進行編譯。 *loadstring* 最典型的用途是:運行外來代碼,例如網絡上的,別人的。。。注意,*loadsting* 只能load語句,不能load表達式, 如果你想算一個表達式的值,那么前面要加上一個*return* 來返回給定表達式的值。下面是一個示例幫助理解: ~~~ print "enter your expression:" local l = io.read() local func = assert(loadstring("return " .. l)) print("the value of your expression is " .. func()) ~~~ 看一下運行情況:(注意第一個為什么報錯了,想想什么才叫表達式) ![](https://box.kancloud.cn/2016-09-06_57ce5ef02e398.PNG) *loadstring* 返回的就是一個普通的函數,可以多次調用: ~~~ print "enter function to be plotted (with variable 'x'):" local l = io.read() local f = assert(loadstring("return " .. l)) for i=1,20 do x = i -- global 'x' (to be visible from the chunk) print(string.rep("*", f())) end ~~~ (*string.rep* 函數復制一個string給定的次數),下面是運行結果: ![](https://box.kancloud.cn/2016-09-06_57ce5ef0464c6.PNG) 如果我們在深究一下,其實不管*loadstring* 也好,*loadfile* 也好,Lua中最基礎的函數是*load* 。*loadfile* 從一個file中加載代碼塊,*loadstring* 從一個string中加載代碼塊,而*load* 調用一個*reader* 函數來獲取代碼塊,這個*reader* 函數分塊返回代碼塊,*load* 調用它,直到它返回**nil**。我們很少使用*load* 函數;通常只有在代碼塊不是位于一個file中,但是又太大了,不適合放到內存中(如果適合放到內存中,那就可以用*loadstring* 了)的時候,才會用load 。 Lua將每一個獨立的代碼塊看作是一個含有不定數量參數的匿名函數。例如,*loadstring*("a = 1")跟下面的表達式基本等價: ~~~ function (...) a = 1 end ~~~ 跟其他函數一樣,代碼塊也可以聲明局部變量: ~~~ f = loadstring("local a = 10; print(a + 20)") f() --> 30 ~~~ 利用這個特性,我們可以重寫上面的一個例子: ~~~ print "enter function to be plotted (with variable 'x'):" local l = io.read() local f = assert(loadstring("local x = ...; return " .. l)) for i=1,20 do print(string.rep("*", f(i))) end ~~~ 在代碼的開始處,加了“local x = ...”,將x聲明為局部變量。調用*f*? 函數的時候,實參i 就變成了變參表達式"..."的值。運行結果,跟上面一樣,就不截圖了。 *load?*函數不會發生運行時錯誤崩潰。如果出錯了,總是返回**nil**+err_msg: ![](https://box.kancloud.cn/2016-09-06_57ce5ef05f4e3.PNG) 一個很常見的誤解:將load代碼塊與定義函數劃等號。在Lua中,函數定義實際上是賦值行為,而且是在運行時才發生,而不是在編譯時。例如,我們有個文件foo.lua: ~~~ function foo (x) print(x) end ~~~ 接著運行下面這條cmd: ~~~ f = loadfile("foo.lua") ~~~ 這個時候,foo被編譯了,但是還沒有被定義。要定義它,必須運行這個代碼塊: ~~~ print(foo) --> nil f() -- defines 'foo' foo("ok") --> ok ~~~ 在生產環境中的程序,在運行外部代碼的時候,要盡可能的捕獲所有的錯誤并作出處理。甚至,如果這些代碼不被信任,那就應該在一個安全的環境中運行,避免在運行這些外來代碼的時候,產生一些不愉快的事情。 ## 2. C代碼 不像Lua代碼,C代碼必須先跟程序鏈接才能被使用。在大多數系統中,做這個鏈接動作的做簡單的方法是:使用動態鏈接功能。那么怎么檢測你的環境是否已經支持這個功能呢? 運行 *print(package.loadlib("a", "b"))*。如果出現類似說文件不存在這樣的錯誤,那么就說明你的環境支持這個動態鏈接功能啦。否則,出現的錯誤信息會告訴你這個功能不支持,或者沒有被安裝。下面是我的環境的表現: ![](https://box.kancloud.cn/2016-09-06_57ce5ef077024.PNG) *package.loadlib* 有兩個string類型的參數,第一個是庫的完整路徑,第二個是函數的名字。因此,一個典型的調用應該跟下面很相似: ~~~ local path = "/usr/local/lib/lua/5.1/socket.so" local f = package.loadlib(path, "luaopen_socket") ~~~ *loadlib* 函數,加載一個給定的庫,并鏈接,但是并沒有去調用那個函數。而是將這個C 函數當作Lua函數返回。如果這個過程出現什么錯誤,那么*loadlib* 會返回**nil**+ err_msg。 要使用*loadlib* 函數,我們必須要給出庫的完整路徑和準確的函數名字,好像有點麻煩。這里有個替代方案,*require* 函數。我們后面在討論這個函數,這里只需知道有這么個東西就可以了。 ## 3. 錯誤 是個人就難免會犯錯。因此我們要盡可能的處理可捕獲的錯誤。因為Lua是一個擴展語言,通常是被嵌入到別的程序(姑且叫做宿主程序吧)中,在出錯的時候,不能簡單的讓它崩潰掉或者退出。而是結束執行當前的代碼塊,并返回到宿主程序中。 Lua遇到任何非期望的條件,都會產生一個錯誤。例如,對非數值進行加運算,調用一個非函數的值,索引一個非table的值,等等。可以顯式地調用error 函數來產生一個錯誤。例如: ~~~ print "enter a number:" n = io.read("*number") if not n then error("invalid input") end ~~~ *if not condition then error end*,在Lua中被封裝成了assert 函數: ~~~ print "enter a number:" n = assert(io.read("*number"), "invalid input") ~~~ *assert*?檢查它的第一個參數,如果為**nil**或者**false**,就產生一個error。第二個參數是可選的。 在函數發現錯誤時,有兩種處理方式,一個是返回error code,另一個是產生錯誤(聯想下C語言中的*assert*)。如何選擇呢?建議如下:可以輕松避免的錯誤,可以通過編碼來修改并規避的,產生錯誤;否則返回errcode。 舉個例子,sin 函數,如果參數用了一個table,假設它返回了一個error code,如果我們需要去檢查一下這個錯誤,那么代碼應該像下面這樣寫: ~~~ local res = math.sin(x) if not res then -- error? <error-handling code> ~~~ 但是,實際上,我們可以在調用sin 函數之前就檢查一下x 是否合法: ~~~ if not tonumber(x) then -- x is not a number? <error-handling code> ~~~ 如果參數x不是一個數值,那么意味著你的程序中某個地方出錯了。這種情況下,停止運行并產生一個錯誤信息,是最簡單有效的方式來處理這個錯誤。 我們再來看下*io.open* 函數,當我去open一個并不存在的file時,會怎么樣呢?這個情況,并不能提前檢查這個file是否存在,因為在很多系統中,要想知道某個file是否存在,只有去嘗試打開它。因此,如果函數*io.open* 因為一些外部原因(例如file does not exist, permisson denied)而不能打開一個file,它會返回**nil**+ err_msg。這樣的話,我們就可以進行一些處理,例如要求用戶重新輸入一個文件名: ~~~ local file, msg repeat print "enter a file name:" local name = io.read() if not name then return end -- no input file, msg = io.open(name, "r") if not file then print(msg) end until file ~~~ 如果僅僅希望保證*io.open* 能夠正常工作,可以簡單的使用: ~~~ file = assert(io.open(name, "r")) ~~~ 這是Lua中的一個習慣用法,如果*io.open*失敗了,就會產生一個錯誤。 ## 4. 錯誤處理和異常 對大多數程序來說,不需要在Lua代碼中進行錯誤處理,宿主程序本身會對錯誤進行相應處理。所有的Lua動作基本都是由宿主程序調用起來的,如果發生錯誤,Lua代碼塊只需要返回相應的err_code,宿主程序本身針對err_code做出相應的處理。在獨立的Lua解析器中,出錯的時候,也只是打印出相應的錯誤信息,然后繼續提示用戶繼續進行運行其他的命令。 如果想要在Lua中對錯誤進行處理,那么必須用*pcall* (protected call)函數來封裝代碼。 假設我們運行一段lua代碼,并捕獲到運行過程中出現的錯誤。我們首先要做的就是封裝這段代碼,假設封裝成函數*foo* : ~~~ function foo () <some code> if unexpected_condition then error() end <some code> print(a[i]) -- potential error: 'a' may not be a table <some code> end ~~~ 然后用*pcall*?去調用這個函數: ~~~ if pcall(foo) then -- no errors while running 'foo' <regular code> else -- 'foo' raised an error: take appropriate actions <error-handling code> end ~~~ 上面的*foo* 函數也可以替換成匿名函數的。 *pcall* 會在protected mode下調用它的第一個參數,以便能夠在函數運行過程中捕獲到出現的錯誤。如果函數運行正常,沒有錯誤產生,*pcall* 返回**true**+ 函數的返回值;如果出現錯誤,*pcall* 返回**false**+ err_msg。 err_msg不一定非得是string,任何傳遞給error 的值都會被pcall 返回,例如下面的示例: ~~~ local status, err = pcall(function () error({code=121}) end) print(status, err.code, type(err)) ~~~ 運行結果如下: ![](https://box.kancloud.cn/2016-09-06_57ce5ef091498.PNG) ## 5. 錯誤信息和堆棧 像上面說的,任何類型的值都可以作為err_msg,但是,通常err_msg還是string類型的,說明發生了什么錯誤。當遇到了內部錯誤(例如試圖索引一個非table值),Lua負責產生err_msg;否則,err_msg就是傳遞給error 函數的值。另外,Lua總是在錯誤發生的地方添加一些位置信息, 如下示例: ~~~ local status, err = pcall(function () a = "a"+1 end) print(err) ~~~ ![](https://box.kancloud.cn/2016-09-06_57ce5ef0acc62.PNG) ~~~ local status, err = pcall(function () error("my error") end) print(err) ~~~ ![](https://box.kancloud.cn/2016-09-06_57ce5ef0c46ab.PNG) 位置信息指明了filename和line number。 *error* 函數有一個額外的參數level, 說明如何獲取錯誤發生的位置。level默認為1,返回error 函數被調用的位置; level 為2, 返回調用*error* 函數的函數被調用的位置; level 為0,不獲取位置信息。對比下下面三段代碼的的區別和執行結果就明白了。 ~~~ function foo(str) if type(str) ~= "string" then error("string expected", 0) --level 0 end print("foo success") end local status, err = pcall(foo(3)) print(err) ~~~ 運行結果: ![](https://box.kancloud.cn/2016-09-06_57ce5ef0d8604.PNG) ~~~ function foo(str) if type(str) ~= "string" then error("string expected", 1) --level 1 end print("foo success") end local status, err = pcall(foo(3)) print(err) ~~~ 運行結果: ![](https://box.kancloud.cn/2016-09-06_57ce5ef0f1a7c.PNG) ~~~ function foo(str) if type(str) ~= "string" then error("string expected", 2) --level 2 end print("foo success") end local status, err = pcall(foo(3)) print(err) ~~~ 運行結果: ![](https://box.kancloud.cn/2016-09-06_57ce5ef114199.PNG) err_test03.lua, err_test04.lua是我code文件的名字而已,可能每個人起名字都不同的。注意一下level 1 和level 2的錯誤行號是不同的。通過這3個例子,很明白了吧。 通常情況下,在程序出錯的時候,可能僅僅知道錯誤發生的位置是不夠的。至少,還需要函數的調用堆棧吧。但是當*pcall* 函數返回的時候,堆棧信息已被部分破壞了。因此,為了獲取堆棧信息,必須在*pcall* 返回之前就建立它。Lua為我們提供了*xpcall* 函數, 它比*pcall* 函數多一個參數*error handler function*。 一旦發生錯誤,Lua在堆棧被損壞之前調用這個*handler* 函數,在*handler* 中,我們可以用*debug* 庫來收集盡可能的有關錯誤的信息。兩個常用的*handler* 函數是*debug.debug* 和*debug.traceback* ;具體用法,后續會詳細討論。 終于寫完這篇了,關電腦睡覺去。 水平有限,如果有朋友發現錯誤,歡迎留言交流
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看