lua的元表

lua的元表,第1张

概述metatable是Lua中的重要概念,每一个table都可以加上metatable,以改变相应的table的行为。让我们看一个例子: t = {} -- 普通的tablemt = {} -- metatablesetmetatable(t, mt) -- 设定mt为t的metatablegetmetatable(t) -- 返回mt 使用getmetatable和setmetatable来

Metatable是Lua中的重要概念,每一个table都可以加上Metatable,以改变相应的table的行为。让我们看一个例子:

t = {} -- 普通的tablemt = {} -- MetatablesetMetatable(t,mt) -- 设定mt为t的MetatablegetMetatable(t) -- 返回mt

使用getMetatablesetMetatable来查看和设定Metatable。当然,上面的代码也可以压缩成一行:

t = setMetatable({},{})

这是因为setMetatable会返回它的第一个参数。

Metatable可以包括任何东西,Metatable特有的键一般以__开头,例如__index__newindex,它们的值一般是函数或其他table。

t = setMetatable({},{  __index = function(t,key)    if key == "foo" then      return 0    else      return table[key]    end  end})
__index

这是Metatable最常用的键了。

当你通过键来访问table的时候,如果这个键没有值,那么Lua就会寻找该table的Metatable(假定有Metatable)中的__index键。如果__index包含一个表格,Lua会在表格中查找相应的键。

other = { foo = 3 }t = setMetatable({},{ __index = other })t.foo -- 3t.bar -- nil

如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。

__newindex

类似__index__newindex的值为函数或table,用于按键赋值的情况。

other = {}t = setMetatable({},{ __newindex = other })t.foo = 3other.foo -- 3t.foo -- nilt = setMetatable({},{  __newindex = function(t,key,value)    if type(value) == "number" then      rawset(t,value * value)    else      rawset(t,value)    end  end})t.foo = "foo"t.bar = 4t.la = 10t.foo -- "foo"t.bar -- 16t.la -- 100

上面的代码中使用了rawgetrawset以避免死循环。使用这两个函数,可以避免Lua使用__index__newindex

运算符

利用Metatable可以定义运算符,例如*

t = setMetatable({ 1,2,3 },{  __mul = function(t,other)    new = {}    for i = 1,other do      for _,v in ipairs(t) do table.insert(new,v) end    end    return new  end})t = t * 2 -- { 1,3,1,3 }

__index__newindex不同,__mul的值只能是函数。与__mul类似的键有:

__add (+) __sub (-) __div (/) __mod (%) __unm 取负 __concat (..) __eq (==) __lt (<) __le (<=) @H_301_131@__call

__call使得你可以像调用函数一样调用table:

t = setMetatable({},{  __call = function(t,a,b,c,whatever)    return (a + b + c) * whatever  end})t(1,4) -- 24

这是很有用的特性。需要以直接调用table的形式调用table中的某个(默认)函数的时候,使用__call设定很方便。例如,kikito的tween.lua,就用了这个技巧,这样直接调用tween就可以调用tween.start。再如MiddleClass中,类的new方法可以通过直接调用类的方式调用。

__tostring

最后讲下__tostring,它可以定义如何将一个table转换成字符串,经常和print配合使用,因为默认情况下,你打印table的时候会显示table: 0x<16进制数字>

t = setMetatable({ 1,{  __tostring = function(t)    sum = 0    for _,v in pairs(t) do sum = sum + v end    return "Sum: " .. sum  end})print(t) -- prints out "Sum: 6"
例子

综合运用以上知识,我们编写一个2D矢量类:

Vector = {}Vector.__index = Vectorfunction Vector.__add(a,b)  if type(a) == "number" then    return Vector.new(b.x + a,b.y + a)  elseif type(b) == "number" then    return Vector.new(a.x + b,a.y + b)  else    return Vector.new(a.x + b.x,a.y + b.y)  endendfunction Vector.__sub(a,b)  if type(a) == "number" then    return Vector.new(b.x - a,b.y - a)  elseif type(b) == "number" then    return Vector.new(a.x - b,a.y - b)  else    return Vector.new(a.x - b.x,a.y - b.y)  endendfunction Vector.__mul(a,b)  if type(a) == "number" then    return Vector.new(b.x * a,b.y * a)  elseif type(b) == "number" then    return Vector.new(a.x * b,a.y * b)  else    return Vector.new(a.x * b.x,a.y * b.y)  endendfunction Vector.__div(a,b)  if type(a) == "number" then    return Vector.new(b.x / a,b.y / a)  elseif type(b) == "number" then    return Vector.new(a.x / b,a.y / b)  else    return Vector.new(a.x / b.x,a.y / b.y)  endendfunction Vector.__eq(a,b)  return a.x == b.x and a.y == b.yendfunction Vector.__lt(a,b)  return a.x < b.x or (a.x == b.x and a.y < b.y)endfunction Vector.__le(a,b)  return a.x <= b.x and a.y <= b.yendfunction Vector.__tostring(a)  return "(" .. a.x .. "," .. a.y .. ")"endfunction Vector.new(x,y)  return setMetatable({ x = x or 0,y = y or 0 },Vector)endfunction Vector.distance(a,b)  return (b - a):len()endfunction Vector:clone()  return Vector.new(self.x,self.y)endfunction Vector:unpack()  return self.x,self.yendfunction Vector:len()  return math.sqrt(self.x * self.x + self.y * self.y)endfunction Vector:lenSq()  return self.x * self.x + self.y * self.yendfunction Vector:normalize()  local len = self:len()  self.x = self.x / len  self.y = self.y / len  return selfendfunction Vector:normalized()  return self / self:len()endfunction Vector:rotate(phi)  local c = math.cos(phi)  local s = math.sin(phi)  self.x = c * self.x - s * self.y  self.y = s * self.x + c * self.y  return selfendfunction Vector:rotated(phi)  return self:clone():rotate(phi)endfunction Vector:perpendicular()  return Vector.new(-self.y,self.x)endfunction Vector:projectOn(other)  return (self * other) * other / other:lenSq()endfunction Vector:cross(other)  return self.x * other.y - self.y * other.xendsetMetatable(Vector,{ __call = function(_,...) return Vector.new(...) end })

原文 Lua Metatables Tutorial
翻译 SegmentFault

总结

以上是内存溢出为你收集整理的lua的元表全部内容,希望文章能够帮你解决lua的元表所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1261607.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存