lua編程之元表與元方法

1、 前言c++

lua是一種很是輕量的動態類型語言,在1993年由由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo等人發明,lua的設計目標是輕便地嵌入宿主語言,加強系統的可擴展性和可定製性。lua的源碼只有兩萬餘行,很是精簡小巧,在目前的腳本引擎中,lua的速度是最快的,這也是lua進入程序設計語言前20名,現在已經普遍應用於遊戲行業,這幾篇文章將會討論下lua的幾個比較重要的特性。算法

一門語言的類型系統是其最根本的特徵,因此本文先從與lua的類型系統關係最緊密的元表和元方法談起。做爲一門輕量級語言,lua的核心很是精簡,它的基本類型只有8種:nil,boolean,number,string,userdata,function,thread和table,其中table是惟一的數據結構,是lua中最重要的類型,能夠做爲其餘數據結構的基礎,如數組,鏈表,隊列和集合等均可以經過table實現。更強大的是,lua還爲table提供了自定義操做的功能。在c++等面嚮對象語言中,類的可操做行爲由成員函數決定。lua中,元方法就是table的「成員函數」,爲不一樣的table提供特殊的操做行爲,元表是元方法的集合。經過元表和元方法,table能夠直接實現類,繼承等面向對象特性。數組

2、 元表元方法介紹數據結構

lua中每一個值其實都有元表,不過每一個table和userdata均可以有本身專有的元表,(userdata是宿主中的數據結構,可使用宿主語言的方法,爲了限制過分對其使用元表,不能在lua腳本中直接設置,需經過lua_setmetatable建立,這裏不討論),而其餘類型的預約義操做都在一個共享的元表中,新的table默認沒有元表,必須經過setmetatable和getmetatable設置和查詢元表。函數

t = {}
assertgetmetatable(t)== nil)  
t1 = {}
setmetatable(t, t1)
assert(getmetatable(t) == t1)

在元表中定義的函數就是元方法,table的元方法分爲算數類,關係類,庫定義和訪問類的元方法。ui

1. 算數類元方法lua

lua的算數類元方法都有對應的字段名,包括__add, __mul,__sub, __div,__mod和__pow等,下面示例瞭如何定義兩個table的加法操做,spa

a = { "a1", "a2","a3" }
b = { "b1", "b2", "b3" }
meta = { }
meta.__add = function(t1, t2)
  t = { }
  for k,  v in ipairs(t1) do
    table.insert(t, v)
  end
  for k, v in ipairs(t2) do
    table.insert(t, v)
  end
  return t
end
setmetatable(a, meta)
c = a+b
for _,v in ipairs(c) do
  print(v)
end

上面代碼中只須要給表a設置了元表,表b沒有元表也能正常運行,這與lua查找元表的順序有關係。lua先查找第一個table,若是有元表而且其中有 __add方法就調用該方法,不關心第二個table有沒有元表;不然查找第二個table有沒有__add的元方法,有就調用第二個table的元方法;若是都沒有這個元方法就引起一個錯誤。設計

2. 關係類元方法code

關係類元方法只有等於__eq,小於__lt和小於等於__le這3個操做,其餘3個會自動轉化,如a>b會自動轉爲b<a.

a = { "a1", "a2" }
b = {  "b1", "b2", "b3" }
meta = { }
meta.__le = function(a, b) 
  for k in pairs(a) do
    if not b[k] then return false end
  end
  return true
end
meta.__lt = function(a, b)
  return a<=b and not (b<=a)
end
meta.__eq = function(a, b)
  return a<=b and b<=a
end
setmetatable(a, meta)
setmetatable(b, meta)
print(a<=b)
print(a<b)
print(a>=a)
print(b>a)
print(b<a)

與算法類元方法不一樣,table必須具備相同的元方法才能用於比較操做。

3. 庫定義的元方法

上面的元方法都是lua核心具備的,是lua虛擬機定義的,除此以外,各類程序庫也會用本身的字段定義元方法,好比print老是調用table的tostring方法,

a = { "a1", "a2" }
meta.__tostring = function(a)
  local l = { }
  for _,k in pairs(a) do
    l[#l+1] = k;
  end
  return "{"..table.concat(l, ",").."}"
end
setmetatable(a, meta)
print(a)

4. 訪問類元方法

訪問元方法使用最廣泛的是__index和__newindex。通常當訪問一個table中不存在的元素時會返回nil,可是若是table具備__index元方法,就不返回nil而是調用這個元方法。利用__index能夠方便地實現繼承,

mt = { }
mt.__index = function(t, k)
  return base[k]
end

base = { b1 = 1, b2 = 2, b3 = 3 }
derive = { d = 4 }

setmetatable(derive, mt)

print(derive.b1)
print(derive.d)

當對table中不存在的索引賦值時就會調用__newindex元方法,

mt = { }
mt.__newindex = function(t, k, v)
  base[k] = v
end
base = { b1 = 1, b2 = 2, b3 = 3 }
derive = { d1 = 4 }
setmetatable(derive, mt)
derive["d2"] = 5
print(base.d2)

3、 元表元方法實例

下面是一個使用元方法的實例,用於產生迭代遞增表,

T = { container = { } }

T.mt = {

  __add = function(a, b)
    local c = T.new{}
    for k,v in pairs(T.new(a)) do
      c[k] = v
    end
    for k,v in pairs(T.new(b)) do
      c[k] = v
    end
    return c
  end,

  __sub = function(a, b)
    local c = T.new{}
    for k,v in pairs(T.new(a)) do
      c[k] = v
    end
    for k,v in pairs(T.new(b)) do
      c[k] = nil
    end
    return c
  end,

  __tostring = function(a)
    local l = { }
    for k in pairs(a) do
      l[#l+1] = k;
    end
    return "{"..table.concat(l, ",").."}"
  end
}

T.new = function(t)
  if (t == nil) then t = {} end
  if (getmetatable(t) == T.mt) then return t end
 local r = {}
  for _, b in ipairs(t) do
    r[tostring(b)] = true
  end
  setmetatable(r, T.mt)
  return r
end

T.print = function(t) 
   for k, v in pairs(t.container) do
     print(k)
     print(v)
   end
 end

local mt = {
  __newindex = function(t, k, v)
    t.container[k] = T.new(v)
  end,

  __index = function(t, k)
    return t.container[k]
  end,
}

setmetatable(T, mt)

T["first"] = { "a1", "b1"}
print("elements in table first")
T.print(T)
T["second"] = T["first"] + { "a2", "b2", "a3", "b3"}
print("elements in table first and  second")
T.print(T)
T["third"] = T["second"] - { "a3", "b3" }
print("elements in table first, second and third")
T.print(T)
相關文章
相關標籤/搜索