Lua學習(二)物件導向

儒雅的Ayo發表於2020-10-30

Lua學習(二)物件導向


一、編譯執行與錯誤處理

(1)錯誤處理

            在Lua中遇到任何未預期的條件都會引發一個錯誤,只要發生錯誤Lua就應該結束當前程式並返回應用程式。我們用error函式來輸出函式的錯誤資訊。下面是學習時用的程式碼:

print("______________________錯誤處理______________________________")
--條件不滿足時執行一些程式碼
--error是一個函式
local name="admin"
if name ~= "admin" then 
    error("NOT ADMIN")
end
--斷言,逗號前參數列示判斷條件,逗號後引數是條件不滿足時執行的error
assert(name=="張三","你不是張三,我要找的人是張三!")

執行的結果:

(2)捕獲錯誤——pcall

            錯誤發生時不希望程式碼停止執行,可以使用pcall捕獲錯誤。

print("______________________錯誤捕獲______________________________")
function test()
    -- body
    print(a[1])
end
--pcall的作用是用來捕獲異常,有兩個返回值,第一個返回值表示狀態,第二個返回值表示返回的資訊,程式沒有出錯時message的值是nil
local status,message=pcall(test)
if status then
    print("Success",message)
else
    print(message)
end

執行結果:

(3)檔案載入

print("______________________檔案載入______________________________")
--loadfile只進行編譯,不執行。loadfile不會引發錯誤,只返回錯誤值,不做錯誤處理
loadfile("./hello,lua")
--dofil能夠執行hello.lua檔案中的程式碼,dofile寫幾次執行幾次
dofile("C:/Users/uesr/Desktop/LuaLearn/hello.lua")
for i=1,10 do
    --require會儲存載入過的檔案,不會重複載入,第一次載入檔案時執行裡面的程式碼
    --require是匯入一個模組,有返回值的,預設返回值boolean型別表示匯入成功或者失敗
    --當require指令碼內部有返回值的時候,返回的就是對應的值
    require("hello")
end
Hello()

hello檔案就輸出一句話:你好!因此執行結果是:你好!

二、元表與元方法

(1)元表與元方法的概念:

              元表是普通的Lua表,定義了原始值在某些特定操作下的行為。例如,當table作為加法的運算元時,Lua檢查其元表中的“__add”欄位是否有個函式。如果 有,Lua呼叫它執行加法。我們稱元表中的鍵為事件(event),稱值為元方法(metamethod)。前述例中的事件是"__add",元方法是執行加法的函式。

 print("____________元表_______________")
local metatable={
    __add=function (t1,t2)
        -- body
        print("兩個相加時候呼叫")
    end,
    __sub=function (t1,t2)
        -- body
        print("兩個相減時候呼叫")
    end
}
local t1={}
local t2={}
setmetatable(t1,metatable)
setmetatable(t2,metatable)
print(getmetatable(t1))
--相加的時候在metatable中查詢__add
local t3 = t1+ t2
--也可以將函式寫在元表外部
local mt = {} 
 mt.__add = function(s1, s2) 
            local result = "" 
            if s1.sex == "boy" and s2.sex == "girl" then 
                       result = "完美的家庭。" 
            elseif s1.sex == "girl" and s2.sex == "girl" then 
                       result = "不好"; 
                else result = "不好"
                end 
            return result
            end
local s1 = {name = "Hello",sex = "boy"} 
local s2 = {name = "Good",sex = "girl"} 
 -- 給兩個table設定新的元表 
 setmetatable(s1, mt); 
 setmetatable(s2, mt); 
 -- 進行加法操作 
 local result = s1 + s2; 
 print(result);

          元表簡單的理解就是我們在獲取不存在元素時,會訪問元表中元方法,就類似其它語言中運算子過載的實現,沒有的功能我們進行擴充套件。

(2)算術類元方法

__add:加法          __sub:減法            __mul:乘法            __div:除法            __unm:相反數           __mod:取模             __pow:乘冪

              注意元方法前面是兩個下劃線哦

注意事項
          1.兩個具有不同元表的值進行算術操作(比如加法)之前舉例的時候,兩個table相加, 這兩個table都是具有相同的元表的,所以沒有任何問題。 那麼,如果兩個table或者兩個進行相加操作的值,具有不同的元表呢? 對於這種情況,Lua 是這樣處理:
a.如果第一個值有元表,就以這個元表為準。
b.否則,如果第二個值有元表,就用第二個值的元表。
c.如果兩個值都沒有元表,或者沒有對應的元方法,那麼,就會報錯。
         2.關係類的元方法 除了加法減法這些算術類的操作之外,大於小於等這些關係類的操作也是有元方法的:
__eq:等於
__lt:小於
__le:小於等於
        如果對兩個具備不同元表的值進行這些比較操作,就會報錯,一定要注意,這和加減法的規 則不一樣。

(3)__index元方法

print("______________元方法__index(用於查詢)______________")
--查詢一個table裡面不存在的欄位的時候,會去元表裡面查詢__index
--__index是函式就呼叫該函式,是table就會在table裡面找,找不到為nil
local t={name="zhangsan"}
print(t.name,t.money)
local mt={
    __index=function (table,key)
        -- body
        print("檢測到呼叫了不存在的欄位:",key)
    end
}
setmetatable(t,mt)
print(t.money)
local mt1={
    __index={money=999}
}
setmetatable(t,mt1)
print(t.money)

輸出結果:

(4)__newindex元方法

print("______________元方法__newindex(用於賦值)______________")
local newmt={
    __index={money=999},
    __newindex=function (t,k,v)
        -- body
        print(k.."不存在,不要對其進行賦值")
    end
}
setmetatable(t,newmt)
--訪問table中的鍵不存在時,呼叫元表中__newindex元方法
t.money=1000
--__newindex並不能完成真正意義上的賦值,因此列印t.money還是會去元表中查詢__index方法
print(t.money)

執行結果:

           有的時候我們想忽略元表__index和__newindex功能,就用rawget函式忽略元表中__index功效,用rawset函式忽略__newindex功效   

三、全域性變數

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

print("____________________全域性變數____________________")
gName="我是全域性變數"
--用三種方式輸出變數的值
print(gName)
print(_G["gName"])
print(_G.gName)
print(type(_G))

執行結果:

四、模組呼叫

print("____________________模組呼叫____________________")
--可以理解成是單例模式,一般用於管理
require("./module")
game.setLevel(10)
print(game.getLevel())

module.lua檔案:

module("game",package.seeall)
local level=1
function setLevel(le)
    level =le
end
function getLevel()
    return level
end
function play()
    print("Play")
    -- body
end
function quit()
    print("Quit")
    -- body
end

輸出結果:   10

五、物件導向

print("__________________Lua物件導向實現__________________")
TSprite={x=0,y=0}
function TSprite.setPosition(self,x,y)
    self.x=x
    self.y=y
end
function TSprite:setPosition(x,y)
    self.x=x
    self.y=y
end
local who=TSprite;
TSprite =nil
--.表示傳的時候要傳引數,:表示傳的時候預設有self(儘量使用:)
who.setPosition(who,1,2) 
--等價於
who:setPosition(2,3)
print(who.x,who.y)

輸出結果

六、繼承實現

print("____________________繼承實現____________________")
Hero ={attack=0}
function Hero:new(o)
    o=o or {}
    setmetatable(o,self)
    --單純的呼叫元表沒有意義,還需要找到元方法
    self.__index=self
    --通過找Hero表的元表元方法,查詢到不存在的值,還沒找到就是真沒有,可以理解是Hero表作為基類
    return o
end
function Hero:skill(addAttack)
    self.attack=self.attack+addAttack
end
--oneHero、twoHero可以理解成根據Hero表例項化的子類
oneHero=Hero:new({attack=100})
twoHero=Hero:new()
--這裡直接列印oneHero、twoHero表,能發現兩個表佔不同的記憶體空間,記憶體地址是不同的
print(oneHero)
print(twoHero)
oneHero:skill(10)
print(oneHero.attack)
--給子類oneHero加自己的方法,可以理解成是子類方法的擴充套件
function  oneHero:test()
    print("test")
end
function oneHero:injured(loseAttack)
    if    loseAttack>self.attack then
        error"not engouth attack"
    end
    self.attack=self.attack-loseAttack/2
end
oneHero:injured(100)
print(oneHero.attack)-->60
oneHero:test()

輸出結果:


學習總結

              在學習Lua語言中元表、元方法時多結合學過的其他語言中的思想,基本的實現思路是一樣的。物件導向思想雖然比較難但是很重要,需要多寫多想。

相關文章