Lua學習筆記1

關於Lua
  1993年由巴西里約熱內盧天主教大學計算機系Roberto Ierusalimschy(萊魯薩利姆斯奇)等人編寫
  一開始就沒有準備去實現C語言已經實現的很是出色的方面
  與C語言之間實現很是好的交互能力,一門膠水語言
  很是適合C程序號學習,互補性很是強
  自動內存管理機制(垃圾回收器),優秀的字符串處理能力,動態大小數據的處理能力   
  簡易性:輕量級,小巧簡單易學,概念很少
  高效:能夠說是目前最快的腳本語言,運行效率很是高,佔用內容不多,源代碼量很是小
  可移植性:沒有使用任何條件編譯處理不一樣平臺,純粹的ANSI C編寫完成,
            任何支持ANSI C的平臺都能編譯經過   
  三種開發形式
        1.獨立使用lua實現功能
        2.使用C語言爲主,lua爲擴展的方式
        3.使用lua爲主,C語言爲擴展的方式
開始
  程序塊chunk
    交互模式裏輸入的一行代
    一個文件裏的代碼
    也就是一連串的語句或命令
    連續的lua語句之間不須要分隔符,但也可使用分號,若是你願意的話
    在交互模式中輸入的每條命令都會當即執行
    使用-i能夠在運行代碼塊後進入交互模式
    使用dofile能夠加載代碼文件進行調試
  詞法規則
    大小寫敏感
    任何字母數字和下劃線,但不能是數字開頭
    儘可能不要使用以單下劃線開頭後跟一個或多個大寫字母的變量,這些大多用於系統保留了
    「_"是啞變量(Dummy Variable)
    and break do else elseif end false for function
    if in local nil not or repeat return then true until while
    "--"爲單行註釋
    --[[   --]]爲多行註釋
    使用---[[能夠很方但的取消大塊註釋
  全局變量
    在任何在方對一個變量賦值就聲明一個全局變量
    直接訪問未聲明的全局變量不會產生錯誤,會返回nil
    要刪除一個全局變量能夠給它賦值nil,一般是沒有必要這麼作的
  局部變量
    local聲明
    僅在聲明的做用域中有效,如do end
    儘可能使用local來聲明變量
 
類型與值
  lua是一種動態類型的語言,在語言中沒有類型定義的語法,每一個值都攜帶了它自身的類型信息
  lua中有8種基礎類型
    nil
      只與自身相等assert(nil==nil),空值
    boolean
      true
      false
      nil和false是假,其它都是真,0和""都是true
    number
      浮點實數,沒有使用整數
    string
      8位編碼也就是個char
      能夠包含任何字符,包括\0,很象stl裏的string類,能夠當二進制流看待
      string.len和#返回的是實現長度,不以\0爲結束標記
      不能夠修改字符串變量內容,只能從新賦值
      lua能高效的處理大字符串,不用懷疑lua字符串的處理能力
      特殊字符與c同樣,如換行是\n tab是\t
      多行字符串定義[[ ]]
      \<ddd>一至三個數據表示字符,ASCII碼
      在字符串上使用數學運算會先前字符串轉換成數字
      ".."是字符串鏈接操做符"wang".."ning"
      在數字後使用字符串鏈接操做符".."時,要有一個空格,否則會認爲是小數點
      儘可能不要依賴lua的自動類型轉換,可使用tostring,tonumber手動轉
    userdata
      一塊由c向lua申請的內存,存在lua裏,申請者自行解析
    function
      lua中的函數是做爲」第一類值「,能夠隨意賦給一個變量
    thread
      協同線程lua沒有真正的多線程,他都是串行執行的,生產者消費者模式
      只能本身中止本身
      可使用守護線程來在多個協同線程中經過狀態判斷來模擬多線程的環境
    table
      lua裏惟一的一種數據結構
      lua裏的module package object都是用table實現的
      table是一個對象,全部對他的
      能夠表示出多種數據結構
        數組
          能夠不寫key添加數據如:t={1,2,3,4}
          自動定義從下標1開始順序向後加一
          全部沒有顯示聲明key的項都是自動生成數組下標的
          lua裏與c不同的地方就是一標都人1開始,必定記住
          t={"a", 2="b", "c", 4=1, 2, 6=3}
          t[1] = "a"
          t[2] = "c"
          t[3] = 2
          t["2"] = "b"
          t["4"] = 1
          t["6"] = 3
          後面三項再也不是數組而是hash表
          使用#能夠獲得數組長度,但要求全部下標是從1開始連續的
          t={}
          t[1] = 1
          t[100] = 100
          的長度是1,由於它以找到nil的值爲結束,以上的定義方式數組斷了
          二維數組就是table套table
          t={ {100,200}, {300,400}}
          t[1][1] = 100
          t[1][2] = 200
          t[2][1] = 300
          t[2][2] = 400
        鏈表
          t1 = {p=t3, v="value", n=t2}
          t2 = {p=t1, v="value", n=t3}
          t3 = {p=t2, v="value", n=t1}
        環形鏈表
        hash表
        隊列
        棧
      可使用lua中的任何類型做key和value,除nil不能當key,當value是刪除
      只能顯示的聲明一個table,t = {}
      添加數據
        t = {"a","b","c",1,2,3}
        t={1="a",2="b",3="c",4=1,4=2,6=3}
        以上兩人個定義不相等,顯示聲明時字符串類型的key能夠不寫雙引號
        t[1] = "a"
        t[2] = "b"
        t[3] = "c"
        t[4] = 1
        t[5] = 2
        t[6] = 3
        這個與第一個相同
        t["1"] = "a"
        t["2"] = "b"
        t["3"] = "c"
        t["4"] = 1
        t["5"] = 2
        t["6"] = 3
        這個與第二個相同
        t={x="lua"}
        t={} t.x="lua" t["x"] = "lua"
  使用type函數能夠返回一個能夠顯示的字符串
 
表達式
  算術操做符
    +(加法) -(減法) *(乘法) /(除法) ^(指數) %(取模) -(負號)
    x%1的結果是x的小數部分,x-x%1是整數部分
  關係操做符
    < > <= >= == ~=
    的有操做符的運算結果都是true或false
    nil僅與nil相等
    不等於與c不同,不是!=
    a={} a.x=1 a.y=0
    b={} b.x=1 b.y=0 
    c = a 
    assert( a==c ) 
    assert( a~=b ) 
    table userdata function僅作引用比較,引用的是一個就同樣
  邏輯操做符
    and
      a and b
      第一個爲false返回第一個值,否則返回第二個值
    or
      a or b
      第一個爲true返回第一個值,否則返回第二個值
      (a and b) or c 至關於c中的a?b:c 但b不爲假,太抽象慎用
    not
      assert(not "wangning" == false)
      assert(not nil == true)
      只會返回true或false
  優先級
    ^
    not # -(負號)
    * / %
    + -
    ..
    < > <= >= ~= ==
    and
    or
語句
    賦值
        多重賦值
        a, b, c, d = 1, 2, 3, 4
        a, b, c = 1, 2
        assert(c == nil)
    控制結構
        if then elseif else end
        while end
        repeat until
        for循環
            數字型for
                for var=exp1,exp2,exp3 do <執行體> end
                exp3能夠不填,默認1
            泛型for
                for k,v in ipairs(t) do <執行體> end
                使用迭代器遍歷
        break和return
            break用於結束循環
            return用於結束函數,後跟返回結果
            任何一個函數的結尾處都有一句隱式的return,因此沒有返回值的函數能夠不用顯示寫出return
            break和return必須是一個塊的最後一條語句或是end else until的前一條語句
            若是確須要在內容中有return或break可使用do end來控制
函數
  定義
    function mytest(a,b,c) <函數體> end
    mytest = function(a,b,c) <函數體> end
    local function mytest(a,b,c) <函數體> end
    local mytest = function(a,b,c) <函數體> end
    t = {} t.mytest = function(a,b,c) <函數體> end
    t = { mytest = function(a,b,c) <函數體> end}
    t = {} function t.mytest(a,c,b) <函數體> end
 
  調用
    基本調用 
        a,b,c = mytest(1,2,3)
    冒號語法糖調用
        o.foo(o,x) = o:foo(x) 
        使用冒號調用時會把冒號前的內容看成函數的第一個參數傳入
    多重返回值
        function mytest()
                return 1,2,3
        end
        a,b = mytest()
        assert(a==1 and b==2)
        a,b,c,d = mytest()
        assert(c==3 and d==nil)
        assert(select("#",mytest()==3))
    變長參數
        function mytest(...)
                a,b=...
                return a,b
        end
        a,b = mytest(1,2)
        assert(a==1 and b==2)
         "..."是表達式,表明變長參數如:return ...就是返回全部傳入的參數
        {...}表明全部變長參數的集合,能夠象使用table的同樣訪問也能夠用ipairs{...}遍歷
        select函數操做訪問變長參數的能力
        select(n, ...)訪問第n個參數
        select("#", ...)得到參數個數
    特殊調用
        當函數僅有一個參數而且是字符串時可使用 mytest "wangning"方式,不用寫()
        當函數僅有一個參數而且是table時可使用 mytest {a=1, b=2, c=3}的方式,不用寫()
    具名實參
        function showwindow(opt)
                print(opt.x)
                print(opt.y)
                 print(opt.title)
                print(opt.text)
        end
        showwindow {x=0, y-0, title="hi!", text="helloworld"}
 
  閉包(詞法域)
    在函數內定義函數,內部函數能夠訪問外部函數的變量
        function mytest()
                local i = 0
                return function()
                i = i + 1
                return i
                end
        end
        my = mytest()
        print(my()) --> 1
        print(my()) --> 2
        my1 = mytest()
        print(my1()) -->1
        print(my1()) -->2
        print(my()) --> 3
    函數式編程的基礎
    能夠實現如迭代器之類的功能,實用性很高
    這種形式就是實現了單一功能的類的對象
 
  尾調用消除 tail-call elimination
    最後一句若是是一個函數調用就是尾調用
    至關於goto,也可用於狀態機
    當前函數真正結束,不會在棧中了
    return <func>(<args>)纔是真正的尾調用
    function f(x) g(x) end不是尾調用,它還會回來
錯誤處理
  assert(exp)
  error("error message text")
  pcall安全調用
協同程序
  lua沒有真正的多線程,都是使用協同程序也實現的多線程
  lua是非對稱式協同程序(semi-coroutine),它提供兩個函數來處理執行權
  任一時刻只能有一個協同程序在執行
  只能本身掛起本身才會中止工做,沒法從外部中止其工做
  與生產者消費者模型同樣互斥協同
  全部協同程序的函數放在coroutine的table裏
  co = coroutine.create(function() print("wangning") end)
  assert(type(co)=="thread")
  create的參數就是協同程序執行的內容,返回一個thread類型的變量
  4種狀態:掛起(suspended)、運行(running)、死亡(dead)、正常(normal)
  剛建立的協同程序處於掛起狀態,能夠用status檢查狀態
  assert(coroutine.status(co)=="suspended")
  coroutine.resume(co)用於啓動或再次啓動協同程序,將狀態改成running
  當協同程序執行完成任務函數後處於dead狀態
  在任務函數中使用coroutine.yield()能夠掛起協同程序
  而後調用coroutine.resume()再次啓動已掛起協同程序
  若是協同程序已經處於dead狀態時,resume會返回false失敗
  當協同程序A喚醒另外一個協同程序B時,A將中止執行,換作B執行
  這時A的狀態爲正常狀態normal
  可使用resume和yield來交換數據
  第一次調用resume的額外參數(第二個之後的參數),都將視爲協同程序任務函數的參數傳入
  以後都將成爲yield的返回值
  resume的第一個返回值表示成功失敗,第二個之後的值都是yield的傳入參數
  當協同程序的任務函數執行完成後,函數的返回值會成爲resume的額外返回值
  co = coroutine.create(function(a,b,c)
      print(a,b,c)
      print(coroutine.yield(4,5,6))
      return 10
  end)
  res, a,b,c = coroutine.resume(co,1,2,3) --> 1 2 3
  print(res,a,b,c) --> true 4 5 6
  res, d = coroutine.resume(co, 7,8,9) --> 7 8 9
  print(res, d) --> true 10
  print(coroutine.resume(co)) --> false cannot resume dead coroutine
  可使用一個協同程序負責主任務循環,判斷其它協同程序狀態,有閒置的就使其執行,實現多線程功能
 
元表與元方法
  基本概念
        1.lua中每一個值都有一個元表
        2.table和userdata能夠有各自獨立的元表
         3.其它類型的值共享其類型所屬的單一元表
         4.lua在建立新table時不會建立元表
         5.其它基本類型的加減等操做都有預約義操做,table只能經過元表及元方法
         6.getmetatable(t)得到table的元表
        7.setmetatable(t, mt)設置table的元表爲mt
        8.t={} mt={} setmetatable(t,mt) assert(getmetatable(t)==mt)
         9.任何table均可以做爲任何值的元表,一組table能夠共享同一個元表
        10.一個table也能夠做爲本身的元表
         11.在lua代碼中只能設置table的元表,其它類型值的元表只能在C代碼中設置
  算術類元方法
         __add(加法)對應"+"操做符
        __sub(減法)對應"-"操做符
        __mul(乘法)對應"*"操做符
        __div(除法)對應"/"操做符
        __unm(相反數) 對應一元"-"操做符
        __mod(取模)對應"%"操做符
        __pow(乘冪)對應"^"操做符
        __concat(鏈接)對應".."操做符
        __len(求長度)對應"#"操做符
         先找第一個值的元方法,若是沒有再找第二個值的元方法,都沒有報錯
  關係類元方法
    __eq(等於)
    __lt(小於)
    __le(小於等於)
    沒有大於和不等於元方法,但能夠轉化實現
           a~=b轉化爲not(a==b)
           a>b轉化爲b<a
           a>=b轉化爲b<=a
  庫定義元方法
    __tostring(字符串轉換)
         tostring函數會用此元方法進行轉換
    __metatable(指向元方法)
         setmetatable、getmetatable會訪問這個元方法
         若是設置成其它內容就能夠起到保護元表的功能
    __mode(弱引用table模式)
        它的值是一個字符串
        若是包含"k"則表示table裏的key是弱引用模式
        若是包含"v"則表示table裏的value是弱引用模式
  table訪問的元方法
    能夠改變table行爲的方法
    __index(訪問表中不存在的字段)
           當沒有這個元方法時訪問不存在字段會返回nil
           當有元方法時兩種訪問形式
                 做爲函數時有兩個參數,第一個是被訪問的table,第二個是不存在的key
                 做爲table時就從這個table裏找被訪問的table裏不存在的這個key
           一般用於實現繼承特性
           做爲函數的時候開銷會大一些,但更靈活,能夠實現多重繼承和緩存等功能
           若是不想涉及元方法,可使用rawget(t,i)"原始訪問",不會加速代碼執行
    __newindex(給表中不存在的字段賦值)
           當沒有這個元方法時會在被訪問的table裏建立新字段並賦值
           當有元方法時兩種訪問形式
                 做爲函數時有三個參數,第一個是被訪問的table,第二個是不存在的key,第三個是value
                 做爲table時,會在這個table裏賦值而不是在被訪問table裏賦值
           可使用rawset(t,k,v)繞過元方法賦值
    能夠利用這兩個元方法實現不少table的特殊功能
           1.具備默認值的table,把帶有值的table做爲__index元方法
           2.跟蹤table的訪問
               t = {} --原table
               local _t = t --私有化訪問
               t = {}  --建立代碼,名字相同
               mt = {}
               mt.__index = function(t,k)
                     print("access "..tostring(k))
                     return _t[k] --訪問原來的table
               end
               mt.__newindex = function(t,k,v)
                     print("update "..tostring(k).." to "..tostring(v))
                     _t[k] = v --更新原來的table
               end
               setmetatable(t, mt)
               但這個例沒法遍歷原來的table,pairs只能操做代理table
           3.只讀table,__index指向被訪問table,__newindex彈錯
環境
  全局變量table
         lua把全部的全局變量存在一個table裏,並把這個table賦值給一個全局變量_G
         _G也在這個全局變量的table裏,它就是一個普通的全局變量
         能夠用這種方法遍歷全部全局變量 for k, v in pairs(_G) do print(k,v) end
         可使用_G["全局變量名"]來訪問全局變量,與直接訪問同樣效果
         能夠經過對_G設置元表來控制對全局變量的訪問行爲
         _G只是個全局變量,對其從新賦值則它就是別的東西了,這種狀況只能經過getfenv找回全局table了
  非全局的環境
         lua中每一個函數均可以有本身的環境
         lua中加載每一個程序塊其實就是把這個程序塊看做是一個函數來調用一下
         每一個程序塊最後均可以有一行return xxx
        若是有require後返回的就是這個xxx
        因此能夠想象一個程序文件被加載就是一個函數被調用了,這個文件的內容整個就是一個大函數
         由於每一個函數均可以有本身的環境,因此每一個程序塊都有本身的環境
        這也正是爲何會命名爲setfenv、getfenv了
        setfenv(f, table)
            1.第一個參數若是是函數則改變這個函數的環境爲第二個參數的table
            2.第一個參數若是是數字,則1表明當前函數,2表明調用本身的函數,以此類推
        getfenv(f)
            1.參數f的使用方法與setfenv同樣
            2.返回當前環境的table
        setfenv(1, {})若是這樣調用則全部環境變量都將沒法訪問,包括如print函數,由於環境table是空的
         繼承環境環境:
            envt = {}
            setmetatable(envt, {__index = _G})
            setfenv(1, envt)
            print("hello")
         在程序塊中調用setfenv(1,table)就會修改當前程序塊的環境
         每一個新建立的函數(包括閉包)的環境都默認繼承了建立者的環境
         當改變當前環境後,後續建立的函數的環境都將隨之改變
         利用默認繼承的功能方便 的實現命名空間之類的功能
         能夠來回切換多個環境來實現多套變量定義和值內容
         函數在查找要訪問的變量時老是以這種順序,局部->全局->環境table元表的__index來查找
         環境主要用於加載模塊等環節,另外的一個主要用途就是看成一個獨立的數據存儲集全
 
模塊與包
  使用require加載模塊
    一個規範的模塊應該返回一個table做爲這個模塊全部導出功能的集合
    lua裏沒經過任何強制性語法規則要求建立模塊時反回一個table
    但最好這麼作,由於大多lua的模塊都是這麼發佈的
    --require的實現源代碼
    function require(name)
        if not package.loaded[name] then
            local loader = findloader(name)
            if loader == nil then
                error("unable to load module "..name)
            end
            package.loaded[name] = true
            local res = loader(name)
            if res ~= nil then
                package.loaded[name] = res
            end
        end
        return package.loaded[name]
    end
    require的內部實現就是經過loadfile或loadlib來加載程序塊
    因此加載模塊就當相於把程序塊看作一個函數
    而後使用模塊的文件名做參數調用一次就加載完成了
    只要使用require加載過一次的模塊就不會再重複加載了,除非手動把package.loaded[name]=nil
    已加載的模塊記錄在package.loaded的table裏
    loader是一個加載器,它會從package.preload裏找對應的加載函數來實施加載
    require"mod"會獲得一個全局的mod變量
    local m = require"mod"能夠重定義導入模塊的名稱
    require的加載路徑如:
    ?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua
    require會以模塊名來替換全部"?",每一個條目用";"隔開
    也就是說require只處理分號和問號
    require用於搜索lua文件的路徑存放在變量package.path中
    當lua啓動後便以環境變量LUA_PATH來初始化這個變量
    若是沒有找到環境變量LUA_PATH就以編譯時定義的一個默認常是值來初始化
    LUA_PATH裏的";;"子串會替換成默認路徑
    若是require找不到lua文件就會去找c程序庫,路徑放在package.cpath裏用LUA_CPATH初始化
    在加載c程序庫裏若是模塊名如a-b,則以b爲模塊加載
    若是一個模塊名爲mod.sub則認爲sub是mod的子模塊,在require時會使用配置好的分隔符替換"."
    若是mod.sub會替換爲mod\sub這樣就能夠很容易的分目錄放置了
  使用module建立模塊
    手工寫法
      --建立模塊--------------------------------------------------------------------------------         --require會把文件名當參數傳進來,用這個變量記下來就好了
      local modname = ...
 
      --建立一個local的table用於記錄導出內容
      local _M = {}
 
      --導出的table就等於這個local的table
      _G[modname] = _M
 
      --有了這句就不用return _G[modname]給require了
      package.loaded[modname] = _M
 
      --設置本環境繼承全局環境,否則鏈接print都不能用,
      --其實若是在模塊中要用全局內容,可能賦給local變量而後使用
      setmatetable(_M, {__index = _G})
 
      --把這個local的table調成當前環境
      setfenv(1, _M)
 
      --模塊內容 ------------------------------------------------------------------------------          --因爲使用了環境,因此local根本就不會進入環境,也就不會導出了
      local function test001()
            print("test001")
      end
 
      --全局函數至關於_M.test002=function()end會導出
      function test002()
            print("test002")
      end
    內建module關鍵字
      --這一句把上面建立模塊部分的代碼都包括了,沒有package.seeall就不繼承全局環境                      module(...,package.seeall)
面向對象編程
   對象的實現
    在lua中table就是一種對象
        1.有本身的狀態
        2.有本身的惟一標識self
         3.有本身的生命週期
    使用table能夠本身實現面向對象的幾乎全部特性
    把函數定義在table中,並使用t.func的形式訪問,如同方法調用
    Account = {balance=0}
    function Account.withdraw(v)
        Account.balance = Account.ballance - v
    end
    但在函數中使用全局的Account是一個很差的習慣
    在lua中使用面向對象方式編程時儘可能使用self和t:func的形式    
    帶有標識自身對象的方法定義:
    function Account.withdraw(self, v)
    同上的語法糖定義:
    function Account:withdraw(v)
    帶有標識自身對象的方法調用:
    a1.withdraw(a1, v)
    同上的語法糖定義:
    a1:withdraw(v)
    使用":"會把自身當作每個參數隱式的傳入
    使用self是面向對象編程的一大核心,不少語言爲程序員隱藏了這個參數
    self在C++中就至關於this指針
   類的實現    
    在lua中沒有類的概念,但能夠本身來實現
    function Account:new(o)
        o = o or {} --若是用戶沒有提供table,則建立一個
        setmetatable(o, self)
        self.__index = self
        return o
    end
    當使用new函數來建立對象後(其實就是建立一個新的table),全部的訪問都會從Account這個table裏找
     這種狀況就至關於Account是一個類,也能夠說是一個原型模具,全部新建立的table都擁有他的屬性和方法
    a = Account:new{balance=0}
    a:deposit(100.00)
    因爲deposit在新建立的table a裏沒有定義
    所以經過它的元表__index來查找,a的元表是Account,
    所以會調用Account的deposit方法,但self傳入的是a
    這就實現了a繼承了Account的方法deposit
    在這裏也看到了使用self來標識調用對象的好處
   繼承和派生
    sa = Account:new()
    s = sa:new{limit=1000.00}
    第一行sa繼承了Account,sa的元表是Account,找不到的方法就去Account裏去找
    第二行s繼承了sa,這裏的new是Account的方法但傳入的self是sa,
    導致s的元表是sa而sa的元表又是Account
    因此一層一層的繼承了下去,而且在每一層的派生table裏均可以定義重載方法和新的方法
    在lua裏的能夠實現多重繼承,就是使元表的__index指向一個函數,而後自行判斷並處理
   私密性
    使用table來實現面向對象的編程方式,幾乎能夠實現全部面向對象的編程特性
    但它沒有也不想去實現的就是對象的私密性,也就是c++裏的private、public、protected
    這與lua設計的初衷有關,lua定位於小型的程序開發,參與一個工程的人不會不少,自行約束
    非要實現私密性的話lua也不是不能,只是不能再使用table和元表的方式了
    可使用函數閉包來實現私密性:
    function newAccount(init)
         local self = {blance=init}
         local withdraw = function(v)
             self.balance = self.balance - v
         end
         local deposit = function(v)
             self.balance = self.balance + v
         end
         return{withdraw = withdraw, deposit = deposit}
    end
    在閉包裏定義一個table的upvalue,而後把全部閉包函數都定義在這裏table裏,
    而後返回這個table,用key訪問內部方法
    使用閉包實現對象的方式比用table效率高並實現了絕對的私密性,但沒法實現繼承,至關於簡單的小對象
    甚至能夠在閉包裏僅定義一個方法,而後經過key來判斷調用是什麼方法
    Tcl/Tk對它的窗口部件就使用這種方法
 
弱引用table
    lua使用自動內存管理機制,經過垃圾回收器來回收內存
    垃圾回收器只能回收它認爲是垃圾的內容,而不能回收用戶認爲是垃圾的內容
    典型的例子棧,棧通常用一個數組和一個表示頂部的索引值表示
    若是彈出一個元素,那麼僅是把棧頂索引減一,
    但這個元素還留在內存在被這棧數組引用着,因此垃圾回收器不知道它是垃圾
    全局變量和table裏的內容會被垃圾回收器回收,只能手動置爲nil
    所以須要一種與回收器能夠協做的機制,這就是弱引用(weak reference)
    lua裏用弱引用table(weak table)來實現這個機制
    3種弱引用table
        1.具備弱引用key的table
        2.具備弱引用value的table
        3.同時具備弱引用key和弱引用value的table
    不管是哪一種類型的弱引用table,只要有一個key或value被回收了
    那麼他們所在的整個條目都會從table中刪除
    table的弱引用類型經過其元表中的__mode字段來決定,這個字段是一個字符串
       1.__mode字段中若是包含"k"則是key的弱引用
       2.__mode字段中若是包含"v"則是value的弱引用
       3.__mode字段中包含"k"和"v"則是key、value的弱引用
             a={}
             b={__mode="k"}
             setmetatable(a,b)    --table a的key就是弱引用
             key={}               --建立第一個key 
             a[key]=1
             key={}               --建立第二個key
             a[key]=2
             collectgarbage()     --強制進行一次垃圾收集 
             for k,v in pairs(a) do print(v) end
              --> 2
    第二次key={}會覆蓋第一個key,這時再沒有對第一個key的引用了,因此會回收
    第二個key因爲還被變量key引用,因此不會回收
    lua只會回收用弱引用table中的對象
    若是key是number、boolean、string則不會回收,因此上例中用table來當key
    可使用弱引用table來實現緩存等機制,熱數據不會被回收,不用的數據自動釋放
math庫
    定義在math中
    全部三角函數都使用弧度
    指數和對數函數
    取整函數
    僞隨機數math.random
        調用時沒有參數返回0~1之間的隨機實數
        調用時僅一個整數參數n,則返回1~n之間的隨機整數
        調用時若是有兩個整數參數m,n,則返回m~n之間的隨機整數
        math.randomseed能夠設置僞隨機數種子
        math.random使用的就是C的rand函數
    數學常量
        pi表示圓周率
        huge表示最大數字
table庫
    table.insert
        插入元素到數據指定位置
        它會移動後續元素以空出空間
        t = {10,20,30}
        table.insert(t, 1, 15)
        --t = {15, 10, 20, 30}
        若是沒有位置參數(第二個參數),將會把元素插到數組末尾
    table.remove
        刪除數組指定位置上的元素,並將該位置後的全部元素前移
        沒有指定位置參數則最後一個元素
    table.sort
        對數組進行排序
        第一個在第二個前則返回true
    table.concat
        鏈接字符串數組中的全部字符串
        有一個參數能夠指定插在每一個字符串之間的分隔符
相關文章
相關標籤/搜索