天天看點

lua裡的一些特殊方法和變量

1.

pcall (f, arg1, ···):

pcall在保護模式(protected mode)下執行函數内容,同時捕獲所有的異常和錯誤。若一切正常,pcall傳回true以及“被執行函數”的傳回值;否則傳回false和錯誤資訊(列印出來即可)。Lua 代碼可以顯式的調用 

error

 函數來産生一條錯誤。成功後的多個傳回值要用多個變量接收, f 後面的都為參數。:不中斷程式

Calls function 

f

 with the given arguments in protected mode. This means that any error inside 

f

 is not propagated; instead, 

pcall

 catches the error and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, 

pcall

 also returns all results from the call, after this first result. In case of any error, 

pcall

 returns false plus the error message.

2.xpcall 接受兩個參數:調用函數、錯誤處理函數。

xpcall (f, err) :不中斷程式

his function is similar to 

pcall

, except that you can set a new error handler.

xpcall

 calls function 

f

 in protected mode, using 

err

 as the error handler. Any error inside 

f

 is not propagated; instead, 

xpcall

 catches the error, calls the 

err

 function with the original error object, and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In this case, 

xpcall

 also returns all results from the call, after this first result. In case of any error, 

xpcall

 returns false plus the result from 

err

.

當錯誤發生時,Lua會在棧釋放以前調用錯誤處理函數,是以可以使用debug庫收集錯誤相關資訊。常用的debug處理函數:debug.debug()和debug.traceback(),前者給出Lua的提示符,你可以自己動手察看錯誤發生時的情況;後者通過traceback建立更多的錯誤資訊,也是控制台解釋器用來建構錯誤資訊的函數。你可以在任何時候調用debug.traceback擷取目前運作的traceback資訊。

3.

_G 存儲全局變量的table

在Lua中,要聲明全局變量很簡單,那就是定義變量的時候,前面不要加上local。這個神秘的全局變量,其實本質上也是一個table,它把我們建立的全局變量都儲存到一個table裡了。而這個table的名字是:_G

A global variable (not a function) that holds the global environment (that is, 

_G._G = _G

). Lua itself does not use this variable; changing its value does not affect any environment, nor vice-versa. (Use 

setfenv

 to change environments.)

 -- 定義一個全局變量

 number = "1";

 -- 用三種方式輸出變量的值

 print(number );

print(_G["number "]);

print(_G.number );

輸出結果如下:

1

1

1

4.改變函數的全局變量環境——setfenv函數

setfenv函數就是用來改變某個函數範圍裡的全局環境的,通俗地說,就是把某個函數範圍内的_G給弄沒了。

setfenv函數兩個參數分别代表:

1). 第一個參數,可以是即将要改變環境的函數,也可以是一個數字。數字1代表目前函數,數字2代表調用目前函數的函數,後面以此類推。

2).第二個參數,新的全局環境table

  -- 定義一個全局變量

  number= "123";

 -- 将目前全局環境重新設定為新的table

 setfenv(1, {});

  -- 輸出值

 print(number);

如果現在運作代碼,輸出結果将會是這樣的:

attempt to call global ‘print’ (a nil value)

為什麼?很出乎意料的臉print函數都無法找到了?

這是因為我們已經把目前函數範圍内的全局變量環境改變了,全局變量預設是儲存在_G中的,而現在的全局變量是在一個新的table裡。目前這個table是空的,是以不存在任何全局變量。

setfenv函數就是用來改變某個函數範圍裡的全局環境的,通俗地說,就是把某個函數範圍内的_G給弄沒了。

setfenv函數兩個參數分别代表:

1). 第一個參數,可以是即将要改變環境的函數,也可以是一個數字。數字1代表目前函數,數字2代表調用目前函數的函數,後面以此類推。

2).第二個參數,新的全局環境table。

5.保留原來的_G

現在連print函數都無法使用了,對于測試很不友善,我們可以做個小動作,把原來的_G保留起來。

如下代碼:

 -- 定義一個全局變量

 number= "123";   

-- 将目前全局環境重新設定為新的table

 setfenv(1, {g = _G})

-- 輸出值

 g.print(number);

   -- 再次定義一個全局變量

  gName = "456";

   -- 再次輸出值

 g.print(gName );

  -- 輸出原來的值

  g.print(g.number);

隻要在定義新的環境時,把_G作為一個字段放到新的table裡,就可以調用原來的全局變量了。

那麼,輸出結果如下:

nil

456

123

三次調用g.print函數的輸出結果都是不一樣的:

a.第一次,此時剛剛重新設定了全局環境,這時候目前函數的全局變量隻有一個,那就是g,是以number的值是nil。

b.第二次,我們再一次對number進行指派,此時,已經在新的環境中了,是以接下來輸出的number值是存在的。

c.第三次,這次輸出的是g.number的值,通過g調用的number值是原先的全局環境裡的值,是以number的值仍然是最初的“123”。

6.最後:使用__index元方法保留原來的_G

這裡還有一個小技巧分享一下,剛剛舉例保留_G,但是調用print等函數時還需要形如g.print的方式,有點礙事。

我們可以利用__index來解決這個問題,如下代碼:

  -- 定義一個全局變量

 number = "123";

 -- 一個table,即将成為新的環境 

 local newG = {};

  setmetatable(newG, {__index = _G});

 -- 将目前全局環境重新設定為新的table

  setfenv(1, newG);   

  number = "456!"; 

 -- 輸出值

  print(number );

  print(_G.number );

我們給新的table設定一個元表,這個元表的__index元方法就是_G。

于是,當新的環境裡找不到print字段時,就會去_G裡尋找。

輸出結果如下

456

123

第一次輸出的是新環境裡的number 值,第二次輸出的是原來環境裡的number 值,互不影響。

7.lua的require函數有兩個特性:1.require函數會搜尋目錄加載檔案.2.require會判斷是否檔案已經加載避免重複加載同一檔案。是以,這個函數隻能加載一次檔案,當我們加載的lua檔案動态改變後,需要重複加載時,隻需要在記載前調用下package.loaded[“檔案名.lua”] = nil就ok了,然後再required(‘檔案名.lua’)。

8.package.path和 package.cpath

package.path用于搜尋自己寫的庫檔案或者第三方的庫檔案。package.path    = "./script/?.lua;" .. package.path

package.cpath用于搜尋自己寫的so庫檔案或者第三方的so庫檔案或者dll檔案。 package.cpath    = "./?.dll;./script/?.dll;" .. package.cpath

對于自定義的子產品,子產品檔案不是放在哪個檔案目錄都行,函數 require 有它自己的檔案路徑加載政策,它會嘗試從 Lua 檔案或 C 程式庫中加載子產品。

require 用于搜尋 Lua 檔案的路徑是存放在全局變量 package.path 中,當 Lua 啟動後,會以環境變量 LUA_PATH 的值來初始這個環境變量。如果沒有找到該環境變量,則使用一個編譯時定義的預設路徑來初始化。

當然,如果沒有 LUA_PATH 這個環境變量,也可以自定義設定,在目前使用者根目錄下打開 .profile 檔案(沒有則建立,打開 .bashrc 檔案也可以)。

當我們調用 require("module") 時就會嘗試打開檔案目錄去搜尋目标。如果找到目标檔案,則會調用 package.loadfile 來加載子產品。否則,就會去找 C 程式庫。搜尋的檔案路徑是從全局變量 package.cpath 擷取,而這個變量則是通過環境變量 LUA_CPATH 來初始。搜尋的政策跟上面的一樣,隻不過現在換成搜尋的是 so 或 dll 類型的檔案。如果找得到,那麼 require 就會通過 package.loadlib 來加載它。

用法:

1 隻加載想要的目錄

package.path = "../myLuaTest/myLuaCode/?.lua;" 

2 增加目錄

package.path = "../myLuaTest/myLuaCode/?.lua;"..package.path  在原有的目錄上加上自己的。 

還有一種方法(并沒有試過)

例如把 "~/lua/" 路徑加入 LUA_PATH 環境變量裡:

#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"
           

檔案路徑以 ";" 号分隔,最後的 2 個 ";;" 表示新加的路徑後面加上原來的預設路徑。

接着,更新環境變量參數,使之立即生效。

source ~/.profile
           

 8.module子產品:module用來來定義子產品

module(...) 操作就相當于一下操作。

local modname = ... 

local M = {}  

_G[modname] = M  

package.loaded[modname] = M     這步是為了消除末尾的return語句,将子產品table直接指派給package.loaded

--[[       和普通Lua程式塊一樣聲明外部函數。       --]]  

setfenv(1,M)  

有一點容易被忽略掉,module 指令運作完後,整個環境被壓棧,是以前面全局的東西再看不見了。比如定義了一個 test 子產品,使用

module("test")

後,下面不再看的見前面的全局環境。如果在這個子產品裡想調用 print 輸出調試資訊怎麼辦呢?一個簡單的方法是

local print=print

module("test")

這樣 print 是一個 local 變量,下面也是可見的。或者可以用

local _G=_G

module("test")

那麼 _G.print 也是可以用的。

注意:關于module( ... , package.seeall)。 一般在一個Lua檔案内以module函數開始定義一個包。module同時定義了一個新的包的函數環境,以使在此包中定義的全局變量都在這個環境中,而非使用包的函數的環境中,也就是說出了這個子產品這個子產品中定義的全局變量不能用。了解這一點非常關鍵。 “module(..., package.seeall)”的意思是定義一個包,包的名字與定義包的檔案的名字相同,并且在包的函數環境裡可以通路使用包的函數環境。使用方式:一般用require函數來導入一個包,要導入的包必須被置于包路徑(packagepath)上。包路徑可以通過package.path或者環境變量來設定。一般來說,目前工作路徑總是在包路徑中。

再調用module函數時,多傳入一個package.seeall的參數,相當于 setmetatable(M, {__index = _G}) 這樣全局的_G就不會消失。

9. collectgarbage:用來控制自動記憶體管理,當設定了setstepmul和setpause,Lua便會開啟自動垃圾回收。

Lua 實作了一個增量标記-掃描收集器。 它使用這兩個數字來控制垃圾收集循環: 垃圾收集器間歇率和垃圾收集器步進倍率。 這兩個數字都使用百分數為機關 (例如:值 100 在内部表示 1 )。

垃圾收集器間歇率控制着收集器需要在開啟新的循環前要等待多久。 增大這個值會減少收集器的積極性。 當這個值比 100 小的時候,收集器在開啟新的循環前不會有等待。 設定這個值為 200 就會讓收集器等到總記憶體使用量達到 之前的兩倍時才開始新的循環。

垃圾收集器步進倍率控制着收集器運作速度相對于記憶體配置設定速度的倍率。 增大這個值不僅會讓收集器更加積極,還會增加每個增量步驟的長度。 不要把這個值設得小于 100 , 那樣的話收集器就工作的太慢了以至于永遠都幹不完一個循環。 預設值是 200 ,這表示收集器以記憶體配置設定的"兩倍"速工作。

如果你把步進倍率設為一個非常大的數字 (比你的程式可能用到的位元組數還大 10% ), 收集器的行為就像一個 stop-the-world 收集器。 接着你若把間歇率設為 200 , 收集器的行為就和過去的 Lua 版本一樣了: 每次 Lua 使用的記憶體翻倍時,就做一次完整的收集。

  • collectgarbage("collect"): 做一次完整的垃圾收集循環。通過參數 opt 它提供了一組不同的功能:
  • collectgarbage("count"): 以 K 位元組數為機關傳回 Lua 使用的總記憶體數。 這個值有小數部分,是以隻需要乘上 1024 就能得到 Lua 使用的準确位元組數(除非溢出)。
  • collectgarbage("restart"): 重新開機垃圾收集器的自動運作。
  • collectgarbage("setpause"): 将 arg 設為收集器的 間歇率 。 傳回 間歇率 的前一個值。
  • collectgarbage("setstepmul"): 傳回 步進倍率 的前一個值。
  • collectgarbage("step"): 單步運作垃圾收集器。 步長"大小"由 arg 控制。 傳入 0 時,收集器步進(不可分割的)一步。 傳入非 0 值, 收集器收集相當于 Lua 配置設定這些多(K 位元組)記憶體的工作。 如果收集器結束一個循環将傳回 true 。
  • collectgarbage("stop"): 停止垃圾收集器的運作。 在調用重新開機前,收集器隻會因顯式的調用運作。

比如下面:

collectgarbage("collect");

collectgarbage("setpause", 100);

collectgarbage("setstepmul", 500);

https://blog.csdn.net/ChinarCSDN/article/details/78667262  sublime3和lua的安裝

10.math.random和math.randomseed

1.math.randomseed接收一個整數 n 作為随機序列種子。對于相同的随機種子, 生成的随即序列一定是相同的。是以程式每次運作, 賦予不同的種子很重要。很自然想到使用系統時間作為随機種子,可以看到前兩次運作的随機數都是一樣的。究其原因,就是 os.time() 傳回的時間是秒級的, 不夠精确, 而 random() 還有個毛病就是如果 seed 很小或者seed 變化很小,産生的随機序列仍然很相似,math.randomseed(tostring(os.time()):reverse():sub(1, 6)),就是把 time傳回的數值字串倒過來(低位變高位), 再取高位6位。 這樣, 即使 time變化很小, 但是因為低位變了高位, 種子數值變化卻很大,就可以使僞随機序列生成的更好一些。https://blog.csdn.net/zhangxaochen/article/details/8095007 

這裡需要注意的是,如果随機時給的随機範圍不同,序列時不一樣的。并且在給定了随機種子的前提下,我們即使不給範圍随機48次,當第49和50次都給随機範圍随機的結果跟我們50次都給範圍随機的結果時一樣的。

 math.random([n [, m]]) 有三種用法: 無參調用, 産生 [0,1) 之間的浮點随機數; 隻有參數 n, 産生 [1,n] 之間的整數; 有兩個參數 [n,m], 産生 [n,m]之間的随機整數.

種子數隻是随機算法的起源數字,和生成的随機數的區間沒有任何關系。

10. 項目中用到loadstring,感覺這個東西挺有用的。例如我們遊戲開啟的時候 擷取伺服器中版本号和其他資訊,這個資訊是一個table,如果你去解析這個字元串的話,比較麻煩。直接用loadstring,該函數的傳回值是傳回一個function,如果load失敗,則傳回nil

local info = 'local map = {a = 1,b = 2};for _,v in pairs(map) do print(v) end'

local fun = loadstring(info )

print(fun())

結果:1 2

另外比如我們存儲一些配置在本地,在我們寫腳本的時候先在腳本加載的時候初始化一個本地table,在用io讀取cfg配置,然後利用loadstring方法傳回這個字元串的函數,然後調用給本地的table指派。這樣就完成了用戶端本地的初始化。

9. assert :當Lua遇到不期望的情況時就會抛出錯誤,比如:兩個非數字進行相加;調用一個非函數的變量;通路表中不存在的值等。你也可以通過調用error函數顯示的抛出錯誤,error的參數是要抛出的錯誤資訊。assert(a,b) a是要檢查是否有錯誤的一個參數,b是a錯誤時抛出的資訊。第二個參數b是可選的。中斷程式

n = io.read()      assert(tonumber(n), "invalid input:" .. n .. "is not a number")

10.unpack  

unpack它接受一個數組(table)作為參數,并預設從下标1開始傳回數組的所有元素。

local info={1,2,3,4,5,6}

local a,b,c,d,e,f=unpack(info)

print(a,b,c,d,e,f)

輸出結果:1   2   3   4   5   6

11.可變參數

function fwrite(fmt, ...)  ---> 固定的參數fmt

    return io.write(string.format(fmt, ...))     

end

fwrite("runoob\n")       --->fmt = "runoob", 沒有變長參數。  

fwrite("%d%d\n", 1, 2)   --->fmt = "%d%d", 變長參數為 1 和 2

輸出結果為 :runoob 12

通常在周遊變長參數的時候隻需要使用 {…},然而變長參數可能會包含一些 nil,那麼就可以用 select 函數來通路變長參數了:select('#', …)或者 select(n, …)

調用select時,必須傳入一個固定實參selector(選擇開關)和一系列變長參數。如果selector為數字n,那麼select傳回它的第n個可變實參,否則隻能為字元串"#",這樣select會傳回變長參數的總數。例子代碼:

執行個體

do  

    function foo(...)  

        for i = 1, select('#', ...) do  -->擷取參數總數

            local arg = select(i, ...); -->讀取參數

            print("arg", arg);  

        end  

    end  

    foo(1, 2, 3, 4);  

end輸出結果為:

arg    1
arg    2
arg    3
arg    4
           

我們知道,其實do ... end就是執行了一個語句塊,并沒有什麼特殊的含義,它基本上等同于C/C++中的{},需要注意的是在這個{}之間的局部變量,在這個區域之後的位置是沒有辦法引用的,在lua中也是一樣的,隻不過在lua中可以随意的定義全局變量,是以在do ... end之間的定義的全局變量,在語句塊之後也可以引用。

do                                                            

    function ss()

        print("printss")

    end

end

print(_G["ss"])  這個會列印出function 因為語句塊裡定義的是全局變量。

local ss 

do 

    function ss()

        print("printss")

    end

end

print(_G["ss"]) 這個列印為nil  因為function ss()是文法糖 等同于 ss=function() 是以相當于在語句塊裡給local ss變量指派,

而列印的是全局table裡的資料。

local ss = 1

do 

    ss = 2

    local aa = 1

end

print(ss) --2  語句塊裡改變了ss的值,

print(aa) -- nil     語句塊裡建立的變量在語句塊外部無效。

12.index和newindex  https://www.jb51.net/article/55155.htm

文章我們介紹了__index元方法,總結來說,__index元方法是用于處理調用table中不存在的字段。

注意,【調用】這個詞,隻是調用,而不是指派。

如果,我們要對table中某個不存在的字段指派呢?(小若:就,直接指派啊!)

沒錯,我們直接就能指派了,不會報錯的。

問題是,如果我想監控這個操作呢?如果有人想對table不存在的字段進行指派的時候,我想進行一些額外的處理呢?

這時候就要用到__newindex。

大家要記住這句話:__index用于查詢,__newindex用于更新。

等會不要混亂了, 初次接觸的話,有可能會混亂。

2.看看普通的指派情況

我們先來看看正常情況下的指派,如代碼:

    local smartMan = {

        name = "none",

        money = 9000000,

        sayHello = function()

            print("大家好,我是聰明的豪。");

        end

    }

    local t1 = {};

    local mt = {

        __index = smartMan,

    }

    setmetatable(t1, mt);

    t1.sayHello = function()

        print("en");

    end;

    t1.sayHello();

這是上一篇用過的例子,一個模仿繼承結構的例子。

來分析一下,mt作為t1的元表,設定__index為smartMan。

于是,當我們調用t1中不存在的字段時,就會自動去smartMan中查找。

比如我們調用了t1.sayHello(),自然能找到對應的函數。

先來看看輸出結果:

[LUA-print] en

我們調用t1的sayHello字段,t1并不存在這個字段(雖然可以通過__index的方式來找到smartMan的sayHello字段)。

但這不影響,給這個字段指派,然後再調用t1.sayHello(),發現是成功的。

這和我們以往的做法一樣,對table做正常的指派操作,不管table本身是否存在這個字段。

監控指派

好了,普通情況我們已經試過了,如果我們想監控table的指派操作呢?

對于不存在的字段,我們不需要被指派呢?想要制作一個隻讀的table呢?

如果你有這些想法,那麼歡迎撥打螢幕下方的号碼,前10位打進的還贈送價值..(小若:停!)

那麼,如果你有這些想法,請看看下面的代碼:

    local smartMan = {

        name = "none",

        money = 9000000,

        sayHello = function()

            print("大家好,我是聰明的豪。");

        end

    }

    local t1 = {};

    local mt = {

        __index = smartMan,

        __newindex = function(table, key, value)

            print(key .. "字段是不存在的,不要試圖給它指派!");

        end

    }

    setmetatable(t1, mt);

    t1.sayHello = function()

        print("en");

    end;

    t1.sayHello();

留意mt元表,我們給它加了一個__newindex。

運作代碼,輸出結果如下:

[LUA-print] sayHello字段是不存在的,不要試圖給它指派!

[LUA-print] 大家好,我是聰明的豪。

很顯然,sayHello字段指派失敗,因為給sayHello字段指派的時候,調用了__newindex元方法,代替了指派操作。

(小若:為什麼?sayHello字段不是存在的麼?為什麼會說不存在呢?)

這裡有一個地方要注意的,t1中确實是不存在sayHello字段的,它隻是因為有元表存在,而元表裡的__index元方法的值是smartMan這個table。

進而,可以在t1找不到sayHello字段的時候,去smartMan中尋找。

但,實際上,t1确實是不存在sayHello字段的,不知道大家能繞明白不?

是以,當試圖給t1的sayHello字段指派時,Lua判定sayHello字段是不存在的,是以會去調用元表裡的__newindex元方法。

__newindex元方法被調用的時候會傳入3個參數:table本身、字段名、想要賦予的值。

隔山打牛,通過給一個table給另一個table的字段指派

和__index一樣,__newindex元方法也可以賦予一個table值。

這種情況下就有點意思了,先看看代碼:

    local smartMan = {

        name = "none",

    }

    local other = {

        name = "大家好,我是很無辜的table"

    }

    local t1 = {};

    local mt = {

        __index = smartMan,

        __newindex = other

    }

    setmetatable(t1, mt);

    print("other的名字,指派前:" .. other.name);

    t1.name = "小偷";

    print("other的名字,指派後:" .. other.name);

    print("t1的名字:" .. t1.name);

這次的代碼和剛剛差不多,但是我們新加了一個other的table,然後把other作為__newindex的值。

于是,當給t1的name字段指派時,就會發生一些奇怪的事情…

先來看看輸出結果:

[LUA-print] other的名字,指派前:大家好,我是很無辜的table

[LUA-print] other的名字,指派後:小偷

[LUA-print] t1的名字:none

當給t1的name字段指派後,other的name字段反而被指派了,而t1的name字段仍然沒有發生變化。

(實際上t1的name字段還是不存在的,它隻是通過__index找到了smartMan的name字段,這個就不唠叨了。)

于是,我們給t1的name指派的時候,實際上是給other的name指派了。

好吧,可憐的other。

總結規則

這就是__newindex的規則:

a.如果__newindex是一個函數,則在給table不存在的字段指派時,會調用這個函數。

b.如果__newindex是一個table,則在給table不存在的字段指派時,會直接給__newindex的table指派。

像__index一樣,如果metamethod是一個表,解釋器對指定的那個表,而不是原始的表進行指派操作,__index會将table和鍵作為參數傳遞進去,_newindex會将mytable, key, value作為參數傳遞。

13.reset和reget :兩者的作用都是繞過元表。有的時候,我們就不想從__index對應的元方法中查詢值,我們也不想更新table時,也不想執行__newindex對應的方法,或者__newindex對應的table。那怎麼辦?在Lua中,當我們查詢table中的值,或者更新table中的值時,不想理那該死的元表,我們可以使用rawget函數,調用rawget(tb, i)就是對table tb進行了一次“原始的(raw)”通路,也就是一次不考慮元表的簡單通路;你可能會想,一次原始的通路,沒有通路__index對應的元方法,可能有性能的提升,其實一次原始通路并不會加速代碼執行的速度。對于__newindex元方法,可以調用rawset(t, k, v)函數,它可以不涉及任何元方法而直接設定table t中與key k相關聯的value v

14. os.time 和os.date

os.time:如果沒有任何參數,就會傳回目前時間。如果參數一個table,并且table的域必須有 year, month, day, 可有也可以沒有 hour, min, sec, isdst,則會傳回table所代表日期的時間,如果未定義後幾項,預設時間為當天正午(12:00:00)。 傳回值是一個 number ,其值取決于你的系統。傳回值通常被用于 os.date 和 os.difftime。os.time(arg)  。作用就是傳回指定日期或者目前的時間戳。注意:傳回的值是一個數字,其含義取決于您的系統。在POSIX、Windows和其他一些系統中,這個數字統計自某個給定的開始時間(“epoch”)以來的秒數。在其他系統中,沒有指定含義,時間傳回的數字隻能用作date和difftime的參數。是以在炫目中需要用于伺服器一直的時間戳來顯示時間。

os.date:第一個參數是時間的格式化參數,如果設定了則會按照固定的格式來格式化時間。若設定time參數,則按time指定的時間格式化,否則按目前時間格式化。如果format 以 ! 開始, date 會被格式化成協調世界時(Coordinated Universal Time) 。 *t 将返一個帶year(4位), month(1-12), day (1--31), hour (0-23), min (0-59), sec (0-61), wday (星期幾, 星期天為1), yday (年内天數), isdst (不包括)(是否為日光節約時間true/false)的表。若沒有*t則傳回一個按C的strftime函數格式化的字元串  若不帶參數,則按目前系統的設定傳回格式化的字元串 .協調世界時為世界統一時間。os.data(format, time)

local time3 = os.date("!*t", os.time())

print(os.time(time3)) 得到的是世界統一時間戳

os.difftime():原型:os.difftime (t2, t1) 解釋:傳回以秒計算的時刻t1到 t2 的內插補點。 在Windows,和其它一些系統中,這個值就等于 t2-t1.

C語言中‘\0’是判定字元數組結束的辨別,表示這串字元到結尾了;例如定義charc[6]=“hello”,而在記憶體中字元數組c則是"hello\0。lua裡的string.len()方法計算字元串長度的時候是吧‘\0’計算在内的,比如"a\000bc\000" 長度是5,而abc長度是3.

15.os.clock  傳回該程式使用的CPU時間(以秒為機關)的近似值。

經過Google搜尋一番,發現lua的os.clock()會調用c的 clock 函數,該函數傳回值依賴于作業系統,但依然傳回一個從啟動到現在的cpu執行時間。 檢視 os.clock 的源碼也驗證了這一點。

clock 

函數有3個關鍵資訊需要關心:

  1. clock 

    傳回cpu滴答次數而非秒數
  2. clock_t clock (void) 

    傳回類型為 

    clock_t 

    ,該類型在32位系統中是4位元組,64位系統中是8位元組
  3. CLOCKS_PER_SEC 

    表示每秒鐘的時鐘滴答次數

在mac上測試得到到 

CLOCKS_PER_SEC 

為1000000,也就是說 

clock()/CLOCKS_PER_SEC 

為執行的秒數,如果在32位系統中有效時間最大為1小時10分鐘。

Android系統都是32位,而且玩家挂機時很容易就能超過1小時的運作時間,是以問題就出在了clock溢出了。 是以隻需要将上面的代碼從 

os.clock 

修改為 

os.time 

就能解決目前時間小于過去時間的問題了。

再談os.clock

上面使用 

os.time 

替換了 

os.clock 

解決了時間倒退的問題,但是丢失了時間的精度,因為 

os.time 

最小精度為1秒。或者使用local socket = require("socket") socket.gettime() 

有沒有什麼地方需要時間精度少于1秒呢,當然是有的,比如掉血後的血條過渡效果。

當顯示血量從100掉到50時,血條需要花費1秒時間從100過渡到50,而不是立刻到達50。這時再用 

os.time 

将不能達到流暢的過渡效果,是以還得使用 

os.clock 

既然 

os.clock 

在32位系統有溢出問題,導緻目前時間小于過去的時間,顯然不能不做處理就直接使用。 由于需要度量的時間很小,不會超過1小時(os.clock在32位系統的最大時間),那麼可以在檢查到溢出時修正目前時間就好。

總結

os.clock 

特點:

  • 可以度量小于1秒的時間
  • 在32位系統中有溢出風險,最大可度量的時間為 4294.967296,約1小時10分鐘
  • 多線程/多程序中不同平台的傳回時間不同 (在兩個clock調用之間加入Sleep(1000),那麼Sleep的時間是算入clock的(Windows平台+VS2008)等)

雖然os.clock有一些限制,但是在遊戲中的一些短時間應用還是沒有問題的。

local function clock(old)     if not old then  return os.clock() end   local now = os.clock()    if now < old then

-- 4294.967296 = math.pow(2, 32)/CLOCKS_PER_SEC

now = now + 4294.967296 end return now end

-- 使用 local begin = clock() local function delatTime() return clock() - begin end

15. lua實作包裹一層的隻讀表 

function readOnly(t)    

    local proxy = {}

    local mt = 

    { -- create metatable

        __index = t,

        __newindex = function (t,k,v)

            error("attempt to update a read-only table", 2)

        end        

    }

    setmetatable(proxy, mt)

    return proxy

end

days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}

print(days[1])

--days[2] = "Noday"

多層table隻讀

-- 用法 local cfg_proxy = read_only(cfg)  retur cfg_proxy

-- 增加了防重置設定read_only的機制

-- lua5.3支援 1)table庫支援調用元方法,是以table.remove table.insert 也會抛出錯誤,

--               2)不用定義__ipairs 5.3 ipairs疊代器支援通路元方法__index,pairs疊代器next不支援故需要元方法__pairs

-- 低版本lua此函數不能完全按照預期工作

  function read_only(inputTable)

    local travelled_tables = {}

    local function __read_only(tbl)

        if not travelled_tables[tbl] then

            local tbl_mt = getmetatable(tbl)

            if not tbl_mt then

                tbl_mt = {}

                setmetatable(tbl, tbl_mt)

            end

            local proxy = tbl_mt.__read_only_proxy

            if not proxy then

                proxy = {}

                tbl_mt.__read_only_proxy = proxy

               local proxy_mt = {

                     __index = tbl,

                     __newindex = function (t, k, v) error("error write to a read-only table with key = " .. tostring(k)) end,

                     __pairs = function (t) return pairs(tbl) end,

                     -- __ipairs = function (t) return ipairs(tbl) end,   5.3版本不需要此方法

                    __len = function (t) return #tbl end,

                    __read_only_proxy = proxy

                 }

               setmetatable(proxy, proxy_mt)

            end

               travelled_tables[tbl] = proxy

            for k, v in pairs(tbl) do

                 if type(v) == "table" then

                    tbl[k] = __read_only(v)

                end

            end

        end

        return travelled_tables[tbl]

    end

    return __read_only(inputTable)

end

local t0 = {k = 1}

local t2 = {

     fdsf = {456}

}

local t1 = {a = {456, 89},   b = {456,ddss = 9, t2 = t2}, d = 45, e = "string",  }

t1.c=t1

local t3 = read_only(t1)

print(t1.a[1])

上面 __pairs = function (t) return pairs(tbl) end, 這句話的意思是重寫 __pairs元方法,因為傳回的table表proxy是,而卻通過

tbl[k] = __read_only(v)  ,已經把原始資料的key指向了新的proxy,而proxy是空的,是以周遊的時候非table的元素可以周遊出來,table值的元素是周遊不出來的,是以要重寫周遊方法來實作周遊我們原資料的表。重寫就是在元表裡定義__pairs元方法後再setmetatable 一下。

16. 文法糖

local t = { x= 1}

t.add = function(self)

    self.x = self.x + 1

end

t.add(t)

print(t.x)                --2

local t = { x= 1}

function t:add()

   self.x = self.x + 1

end

t:add()

print(t.x)            --2     下面的為文法糖格式,隐式的增加了一個self作為第一個參數 調用從 ‘。’變為“:”

17 閉包函數   閉包綁定局部變量時是引用綁定,多個閉包可以共享同一個局部變量

function get_closure()

    local x = 1

    local add = function() x = x + 1  end

    local get = function() return x  end

    --add 和 get 使用的是同一個x

    return get, add

end

local get, add = get_closure()

print(get()) --1

add()

add()

print(get()) --3

18.在調用lua函數時,也需要将對應的參數放在一對圓括号中,即使調用函數時沒有參數,也必須寫出一對空括号。對于這個規則隻有一種特殊的例外情況:一個函數若隻有一個參數,并且此參數是一個字元串或table構造式,那麼圓括号便可以省略掉。

繼續閱讀