Lua 學習筆記之二(進階話題)

2023-03-11 13:56 更新

進階話題

1.函數(shù)閉包

1.1 實例代碼

    function createCountdownTimer(second)
        local ms = second * 1000  --ms為countDown的Upvalue
        local function countDown()
            ms = ms -1
            return ms
        end

        return countDown
    end

    local timer1 = createCountdownTimer(1) 

    for i = 1, 3 do
        print(timer1()) 
    end

輸出結果:

999

998

997

1.2 關于函數(shù)閉包描述

  • Upvalue

    一個函數(shù)所使用的定義在它的函數(shù)體之外的局部變量(external local variable)稱為這個函數(shù)的upvalue。 在前面的代碼中,函數(shù)countDown使用的定義在函數(shù)createCountdownTimer 中的局部變量ms就是countDown的upvalue,但ms對createCountdownTimer而 言只是一個局部變量,不是upvalue。 Upvalue是Lua不同于C/C++的特有屬性,需要結合代碼仔細體會。

  • 函數(shù)閉包

一個函數(shù)和它所使用的所有upvalue構成了一個函數(shù)閉包。 

  • Lua函數(shù)閉包與C函數(shù)的比較

Lua函數(shù)閉包使函數(shù)具有保持它自己的狀態(tài)的能力,從這個意義上說,可以 與帶靜態(tài)局部變量的C函數(shù)相類比。但二者有顯著的不同:對Lua來說,函數(shù) 是一種基本數(shù)據(jù)類型——代表一種(可執(zhí)行)對象,可以有自己的狀態(tài);但 是對帶靜態(tài)局部變量的C函數(shù)來說,它并不是C的一種數(shù)據(jù)類型,更不會產(chǎn)生 什么對象實例,它只是一個靜態(tài)地址的符號名稱。

2. 基于對象的實現(xiàn)方式

2.2  實例代碼

    local function create(name ,id )
        local data = {name = name ,id = id}  --data為obj.SetName,obj.GetName,obj.SetId,obj.GetId的Upvalue
        local obj = {}  --把需要隱藏的成員放在一張表里,把該表作為成員函數(shù)的upvalue。

        function obj.SetName(name)
            data.name = name 
        end

        function obj.GetName() 
            return data.name
        end

        function obj.SetId(id)
            data.id = id 
        end

        function obj.GetId() 
            return data.id
        end

        return obj
    end

輸出結果:

mycreate's id:1mycreate's name:Sam

mycreate's id:1mycreate's name:Lucy

2.2  有關對象實現(xiàn)的描述

實現(xiàn)方式:  把需要隱藏的成員放在一張表里,把該表作為成員函數(shù)的upvalue。

局限性:  基于對象的實現(xiàn)不涉及繼承及多態(tài)。但另一方面,腳本編程是否需要繼承和多態(tài)要視情況而定。 

3.元表

3.1  實例代碼(1):

    local t = {}
    local m = {a = "and",b = "Li Lei", c = "Han Meimei"}

    setmetatable(t,{__index = m})  --表{ __index=m }作為表t的元表

    for k,v in pairs(t) do  --窮舉表t
        print("有值嗎?")
        print(k,"=>",v)
    end

    print("-------------")
    print(t.b, t.a, t.c)

輸出結果:


Li LeiandHan Meimei

3.2  實例代碼(2):

    local function add(t1,t2)
        --‘#’運算符取表長度
        assert(#t1 == #t2)
        local length = #t1
        for i = 1,length do
            t1[i] = t1[i] + t2[i]
        end
        return t1
    end

    --setmetatable返回被設置的表
    t1 = setmetatable({1,2,3},{__add = add})
    t2 = setmetatable({10,20,30},{__add = add})

    for k,v in  pairs(t1) do
        print(k,"=>",v)
    end

    for k,v in  pairs(t2) do
        print(k,"=>",v)
    end

    print("---------兩元表相加--------------")
    t1 = t1 + t2
    for i = 1 ,#t1 do
        print(t1[i])
    end

輸出結果:

1=>1

2=>2

3=>3

1=>10

2=>20

3=>30

---------兩元表相加--------------

11

22

33

3.3  有關元表的描述:

定義 :

元表本身只是一個普通的表,通過特定的方法(比如setmetatable)設置到某個對象上,進而影響這個對象的行為;一個對象有哪些行為受到元表影響以及這些行為按照何種方式受到影響是受Lua語言約束的。比如在前面的代碼里,兩個表對象的加法運算,如果沒有元表的干預,就是一種錯誤;但是Lua規(guī)定了元表可以“重載”對象的加法運算符,因此若把定義了加法運算的元表設置到那兩個表上,它們就可以做加法了。元表是Lua最關鍵的概念之一,內容也很豐富,請參考Lua文檔了解詳情。

元表與C++虛表的比較:

如果把表比作對象,元表就是可以改變對象行為的“元”對象。在某種程度上,元表可以與C++的虛表做一類比。但二者還是迥然不同的:元表可以動態(tài)的改變,C++虛表是靜態(tài)不變的;元表可以影響表(以及其他類型的對象)的很多方面的行為,虛表主要是為了定位對象的虛方法(最多再帶上一點點RTTI)。 

4.  基于原型的繼承

4.1 實例代碼

    local Robot = { name = "Sam", id = 001 }
    function Robot:New(extension)
        local t = setmetatable(extension or { }, self) 
        self.__index = self
        return t
    end

    function Robot:SetName(name)
        self.name = name 
    end

    function Robot:GetName() 
        return self.name
    end

    function Robot:SetId(id)
        self.id = id end

    function Robot:GetId() 
        return self.id
    end

    robot = Robot:New()
    print("robot's name:", robot:GetName()) 
    print("robot's id:", robot:GetId()) 
    print("-----------------")

    local FootballRobot = Robot:New({position = "right back"})
    function FootballRobot:SetPosition(p) 
        self.position = p
    end

    function FootballRobot:GetPosition()
        return self.position 
    end

    fr = FootballRobot:New()
    print("fr's position:", fr:GetPosition()) 
    print("fr's name:", fr:GetName()) 
    print("fr's id:", fr:GetId()) 
    print("-----------------")

    fr:SetName("Bob")
    print("fr's name:", fr:GetName()) 
    print("robot's name:", robot:GetName())

輸出結果:

robot's name:Sam

robot's id:1


fr's position:right back

fr's name:Sam

fr's id:1


fr's name:Bob

robot's name:Sam

4.2 相關描述:

prototype模式一個對象既是一個普通的對象,同時也可以作為創(chuàng)建其他對象的原型的對象(即類對象,class object);動態(tài)的改變原型對象的屬性就可以動態(tài)的影響所有基于此原型的對象;另外,基于一個原型被創(chuàng)建出來的對象可以重載任何屬于這個原型對象的方法、屬性而不影響原型對象;同時,基于原型被創(chuàng)建出來的對象還可以作為原型來創(chuàng)建其他對象。 

5.包

5.1 實例代碼:

hello.lua

    local pack = require "mypack"  --導入包[注:包的名字與定義包的文件的名字相同(除去文件名后綴,在前面的代碼中,就是“mypack”)]

    print(ver or "No ver defined!")
    print(pack.ver)

    pack.aFunInMyPack()

    print(aFunInMyPack or "No aFunInMyPack defined!")
    aFuncFromMyPack()

mypack.lua

module(..., package.seeall) --定義包
ver = "0.1 alpha"

function aFunInMyPack() 
    print("Hello!")
end

_G.aFuncFromMyPack = aFunInMyPack

輸出結果:

No ver defined!

0.1 alpha

Hello!

No aFunInMyPack defined!

Hello!

5.2有關包的描述:

  • 定義

包是一種組織代碼的方式。

  • 實現(xiàn)方式

一般在一個Lua文件內以module函數(shù)開始定義一個包。module同時定義了一個新的包的函數(shù)環(huán)境,以使在此包中定義的全局變量都在這個環(huán)境中,而非使用包的函數(shù)的環(huán)境中。理解這一點非常關鍵。以前面的代碼為例, “module(..., package.seeall)”的意思是定義一個包,包的名字與定義包的文件的名字相同(除去文件名后綴,在前面的代碼中,就是“mypack”),并且在包的函數(shù)環(huán)境里可以訪問使用包的函數(shù)環(huán)境(比如,包的實現(xiàn)使用了print,這個變量沒有在包里定義,而是定義在使用包的外部環(huán)境中)。

  • 使用方式

一般用require函數(shù)來導入一個包,要導入的包必須被置于包路徑(packagepath)上。包路徑可以通過package.path或者環(huán)境變量來設定。一般來說,當前工作路徑總是在包路徑中。

 現(xiàn)在官網(wǎng)已經(jīng)不提倡使用module了,官方給出了兩個理由(以?model(... , package ,seeall)?為例):

package.seeall 這種方式破壞了模塊的高內聚,原本引入 "filename" 模塊只想調用它的 foobar() 函數(shù),但是它卻可以讀寫全局屬性,例如 "filename.os"。

module 函數(shù)壓棧操作引發(fā)的副作用,污染了全局環(huán)境變量。例如 module("filename") 會創(chuàng)建一個 filename 的 table,并將這個 table 注入全局環(huán)境變量中,這樣使得沒有引用它的文件也能調用 filename 模塊的方法。

  • 其他

請參考Lua手冊進一步了解包的詳細說明。 

參考文獻《C/C++程序員的Lua快速入門》


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號