Lua--table、模块、元表与元方法、面向对象

table

数组

table 可以定义一维、二维、多维数组。但是数组的索引从1开始,无需声明数组长度

cities={"北京","上海","广州"}
cities[4]="南京"

同一数组的元素可以是任意类型。

map

table可以定义出类似map的key-value数据结构。

访问时可通过key访问,也可以指定索引来访问。

emp={name="张三",age=22,dept="IT"}
emp["gender"]="男"
print(emp["name"])

emp.addr="北京"
print(emp.age)
-- 也可以使用表达式作为key ,表达式需要中括号括起来。
arr= {
    ["emp_".."xxx"] = true,
    [2+3] = "hello",
    ["hi"] = 123
}

print(arr.emp_xxx) -- true
print(arr[5]) -- hello
print(arr.hi) -- 123

混合结构

将数组和key-value 混合在一个table中, key-value不会占用数组的数字索引值。

emp={"北京",name="张三","上海",age=22,"广州",dept="IT"}

print(1) -- 北京
print(2) -- 上海
print(3) -- 广州

table操作函数

  • table.concat() 字符串连接

    table.concat(table[,sep[,start[,end]]])
    emp={"北京",name="张三","上海",age=22,"广州",dept="IT"}
    table.concat(emp,",",1,2) -- "北京,上海"
    

    将指定的table 数组元素 进行字符串连接。

    连接从start索引位置到end索引位置的所有数组元素,元素间使用sep隔开。

    如果table是一个混合结构,那么这个连接与key-value无关,仅是连接数组元素

  • table.unpack() 拆包

    返回指定table的数组中从第i个元素到第j个元素的值。i和j可选,默认i=1,j=最后一个元素。

    emp={"北京","上海","广州","深圳","大连"}
    print(table.unpack(emp,2,3)) -- 上海    广州
    
  • table.pack() 打包

    此函数是一个可变参。将指定参数打包成一个table返回。

    返回的table中有一个属性n,表示table包含元素的个数。

    t=table.pack("北京","上海","广州","深圳","大连")
    print(t.n) -- 5
    
  • table.maxn() 返回table中的数组的最大索引,即数组包含元素的个数。

  • table.insert() 指定table的数组部署指定位置插入一个元素,其后面的元素会后移。不指定位置,就插入到数组部分末尾。

    t=table.pack("北京","上海","广州","深圳","大连")
    table.insert(t,2,"郑州")
    table.insert(t,"成都")
    
  • table.remove() 删除并返回table数组部分指定位置的元素。其后面的元素会前移。不指定位置,默认删除数组中的最后一个元素。

    table.remove(t,3)
    
  • table.sort() 指定table的数组元素升序排序,也可以按照指定函数的规则进行排序。

    注意:

    • 如果arr中既有字符串,又有数值型,那么排序会报错。
    • 如果数组中多个元素相同,那么相同的元素排序结果不确定。
    • 如果数组元素中包含nil,那么排序会报错。

    迭代器

    Lua有两个迭代器pairs(table)与ipairs(table),用于泛型for循环

    • pairs(table):迭代整个table元素,无论是数组元素,还是key-value
    • ipairs(table):只迭代table中的数组元素。
    emp={"北京",name="张三","上海",age=22,"广州",dept="IT"}
    for i,v in ipairs(emp) do
        print(i,v)
    end
    -- 输出结果如下:
    1   北京
    2   上海
    3   广州
    
    for i,v in pairs(emp) do
        print(i,v)
    end
    -- 输出结果如下:
    1   北京
    2   上海
    3   广州
    name  张三
    age   22
    dept  IT
    

    模块

    Lua可以把一些公用的代码放在一个文件,以API接口的形式在其他地方调用,有利于代码复用和降低代码耦合度。

    模块文件主要由table组成。在table中添加相应的变量、函数,最后文件返回该table即可。

    如果其他文件中需要使用该模块,通过require导入该模块即可。

    定义模块

    -- 定义一个模块
    rectangle = {}
    -- 为模块添加一个变量
    rectangle.pi = 3.14
    -- 为模块添加函数
    function rectangle.perimeter(a,b)
        return (a+b)*2
    end
    -- 以匿名函数的方式为模块添加一个函数
    rectangle.area= function (a,b)
        return a*b
    end
    -- 定义一个全局变量(与模块无关)
    goldenRatio = 0.618
    -- 定义一个局部函数(与模块无关)
    local function circulaArea(r)
        return rectangle.pi*r*r
    end
    -- 定义一个全局函数(与模块无关)
    function maxCirculArea(a,b)
        local r =math.min(a,b)
        return circulaArea(r)
    end
    
    return rectangle
    

    将以上文件保存为rectangle.lua

    使用模块

    require ("文件路径")或者 require “文件路径”,可以省略括号。

    其中文件名不能写.lua扩展名。

    require()函数有返回值,返回的是模块文件最后return的table。可以使用一个变量接收这个table作为模块的别名,后面就可以使用别名来访问模块了。

    -- 导入模块
    rect = require "rectangle"
    -- 访问模块属性
    print(rectangle.pi)
    print(rect.pi)
    -- 访问与模块无关内容
    print(goldenRatio)
    print(maxCirculArea(3,5))
    

    再看模块

    模块文件中还可以定义其他与table无关的内容。这些全局变量与函数就是普通的全局变量与函数,与模块无关,其会随着模块的导入而同时导入。

    所以在使用时可以直接使用,无需也不能添加模块名称。

    元表与元方法

    元表:普通table的元数据表

    元方法:元表中定义的普通表的默认行为。

    ·每个普通table都可为其定义一个元表,用于扩展其行为。

    比如:对于table与数值相加的行为,Lua没有定义,用户可通过为其指定元表来扩展。

    或者当用户访问不存在的table元素,Lua默认返回nil,此时可以给table指定元表来扩展该行为,给用户提示,并返回指定的值。

    重要函数

    • setmetatable(table,metatable):将metatable指定为普通表table的元表。

    • getmetatable(table):获取普通表的元表。

      __index元方法

    当用户对table进行访问,访问的数组索引或者key不存在,系统就会自动调用元表的__index元方法。该重写的方法可以是一个函数,也可以是另一个表。如果重写的__index元方法是函数,且有返回值,则直接返回;如果没有返回值,则返回nil。

    emp={"北京",name="张三","上海",age=22,"广州",dept="IT"}
    -- 声明一个元表
    meta = {}
    -- 将table与元表相关联
    setmetatable(emp,meta)
    -- 有返回值的情况
    meta.__index = function (tab,key)
        return "访问"..key.."的值不存在"
    end
    
    -- 或者无返回值的情况
    meta.__index = function (tab,key)
        print("访问"..key.."的值不存在") 
    end
    
    print(emp.x)
    print(emp[2]])
    

    __index 元方法是另一个table的情况

    emp={"北京",name="张三","上海",age=22,"广州",dept="IT"}
    -- 声明一个元表
    meta = {}
    -- 将table与元表相关联
    setmetatable(emp,meta)
    -- 再定义一个普通表
    other= {}
    other[5] = "成都"
    other[6] = "重庆"
    -- 指定元表为另一个普通表
    meta.__index = other
    -- 在原始表中找不带,会到元表指定的普通表中查找
    print(emp[6]) -- 重庆
    

    __newindex元方法

    当为table中一个不存在的索引或key赋值时,会自动调用元表的__newindex元方法。

    重写该方法可以是一个函数,也可以是另一个表。

    如果重写__newindex元方法是函数,且有返回值,则直接返回;无返回值,就返回nil。

    emp={"北京",name="张三","上海",age=22,"广州",dept="IT"}
    
    -- 声明一个元表
    meta = {}
    -- 将原始表与元表关联
    setmetatable(emp,meta)
    -- 无返回值情况
    function meta.__newindex(tab,key,value)
        print("新增的key="..key..",value="..value)
        -- 将新增的key-value加入到原始表
        rawset(tab,key,value)
    end
    
    emp.x = "青岛"
    print(emp.x)
    
    emp={"北京",name="张三","上海",age=22,"广州",dept="IT"}
    
    -- 声明一个元表
    meta = {}
    -- 将原始表与元表关联
    setmetatable(emp,meta)
    
    -- 定义一个普通表
    other = {}
    meta.__newindex = other
    
    emp.x = "大连"
    print(emp.x) -- nil
    print(other.x) -- 大连
    

    运算符元方法

    如果要为一个表扩展+、-、>、==等运算功能,可重写对应的元方法。

    比如:要扩展+运算功能,可重写__add元方法。这样当一个table在进行加法运算时,就会自动调用其元表的__add元方法

    emp={"北京",name="张三","上海",age=22,"广州",dept="IT",32 ,11}
    
    -- 声明一个元表
    meta = {
    	__add = function (tab,num)
    		for k,v in pairs(tab) do
    			if type(v) == "number" then
    				-- 若value是数值类型,就做加法
    				tab[k] =v+num
    			end
    		end
    		-- 返回变化过的table
    		return tab
    	end
    }
    -- 将原始表与元表关联
    setmetatable(emp,meta)
    
    empsum = emp + 5
    

    其他的运算符元方法还有 减法、乘法等,可以在Lua官网查询

无标1题

__tostring元方法

直接输出一个table,输出的是table的内存地址,如果要输出其内容,可重写__tostring元方法

emp={"北京",name="张三","上海",age=22,"广州",dept="IT",32 ,11}

-- 声明一个元表
meta = {
	__add = function (tab,num)
		for k,v in pairs(tab) do
			if type(v) == "number" then
				-- 若value是数值类型,就做加法
				tab[k] =v+num
			end
		end
		-- 返回变化过的table
		return tab
	end,
	__tostring = function (tab)
		str = ""
		for k,v in pairs(tab) do
			str = str.." "..k..":"..v
		end
		return str
	end
}
-- 将原始表与元表关联
setmetatable(emp,meta)

print(emp)

__call元方法

当把一个table以函数形式来使用时,会调用重写的__call元方法。

该用法可以简化对table的操作,将对table的操作与函数直接结合。

emp={"北京",name="张三","上海",age=22,"广州",dept="IT",32 ,11}

-- 声明一个元表
meta = {
	__add = function (tab,num)
		for k,v in pairs(tab) do
			if type(v) == "number" then
				-- 若value是数值类型,就做加法
				tab[k] =v+num
			end
		end
		-- 返回变化过的table
		return tab
	end,
	__tostring = function (tab)
		str = ""
		for k,v in pairs(tab) do
			str = str.." "..k..":"..v
		end
		return str
	end,
	__call = function (tab,num,str)
		for k,v in pairs(tab) do
			if type(v) == "number" then
				tab[k] = v+num
			elseif type(v) == "string" then
				tab[k] = v..str
			end
		end
		return tab
	end
}
-- 将原始表与元表关联
setmetatable(emp,meta)

newemp = emp(3,"-haha")
for k,v in pairs (newemp) do
	print(k..":"..v)
end

元表单独定义

可以将元素单独定义为一个文件。文件中仅可定义一个元表,且一般文件名与元表名称相同。

若一个文件要使用其他文件中定义的元表,需要使用require "元表文件名" 即可。

如果想扩展该元表而不想修改元表文件,可以在用户自己文件中重写其对应功能的元方法即可。

-- 声明一个元表
meta = {
	__add = function (tab,num)
		for k,v in pairs(tab) do
			if type(v) == "number" then
				-- 若value是数值类型,就做加法
				tab[k] =v+num
			end
		end
		-- 返回变化过的table
		return tab
	end,
	__tostring = function (tab)
		str = ""
		for k,v in pairs(tab) do
			str = str.." "..k..":"..v
		end
		return str
	end,
	__call = function (tab,num,str)
		for k,v in pairs(tab) do
			if type(v) == "number" then
				tab[k] = v+num
			elseif type(v) == "string" then
				tab[k] = v..str
			end
		end
		return tab
	end
}

将以上内容保存为meta.lua,需要使用的时候直接引入但后关联就可。

-- 导入元表
require "meta"
emp={"北京",name="张三","上海",age=22,"广州",dept="IT",32 ,11}
-- 将原始表与元表关联
setmetatable(emp,meta)

-- 重写__index元方法
meta.__index = function(tab,key)
   print("访问的"..key.."不存咋") 
end

newemp = emp(3,"-haha")
for k,v in pairs (newemp) do
	print(k..":"..v)
end

面向对象

Lua没有类的概念,但通过table、function、元表 可以模拟和构造出类这样的功能结构。

简单对象的创建

通过table与function 可以创建一个简单的Lua对象:table为Lua对象赋予属性,function 为Lua 对象赋予行为。

-- 创建一个名称为animal的对象
animal = {
    name = "dog",
    age = 3,
    color = "black",
    bark = function ()
        print("woof!")
    end
}
animal.bark()	

类的创建

通过table、function与元表可以定义类:

使用一个table作为基础类

使用一个function 作为此基础类的new()方法

在new()方法中创建一个空表,再为该空表指定一个元表。

此元表重写__index 元方法,且将基础表指定为重写的__index元方法。

由于new()中的表是空表,所以访问的所有key都会从基础表中查找。

-- 创建一个名称为animal的类
Animal = {
    name = "no_name",
    age = 0
}

function Animal:bark(voice)
    print(self.name.."叫了一声"..voice)
end

-- 创建一个无参构造器
function Animal:new()
    -- 创建一个空表 来存储对象
    local obj = {}
    -- 为新表设置元表为AnimalL
    -- 在新表中找不到key时,就会去AnimalL中找到key
    setmetatable(obj,self)
    self.__index = self
    return obj
end

-- 添加一个含参数的构造器
function Animal:new(name,age,type)
    local obj = {}
    setmetatable(obj,self)
    self.__index = self
    obj.name = name
    obj.age = age
    obj.type = type
    return obj
end

animal = Animal:new()
-- 给对象赋值,下面的name、age、type 实际上都加到了新表中,而不是AnimalL中
animal.name = "旺财"
animal.age = 3
animal.type = "狗"
print(animal.name.."的年龄是"..animal.age.."岁了,它是一只"..animal.type)
animal:bark("汪汪")
-- 调用带参数的构造器创建对象
animal2 = Animal:new("小花",2,"猫")
print(animal2.name.."的年龄是"..animal2.age.."岁了,它是一只"..animal2.type)
animal2:bark("喵喵")

-- 为Animal创建一个子类
Cat = Animal:new()
Cat.type="猫"
Cat.eyes = "绿眼睛"
function Cat:catchMouse()
    print(self.name.."抓住了一只老鼠")
end
tomcat = Cat:new("汤姆",5)
print(tomcat.name.."的年龄是"..tomcat.age.."岁了,它是一只"..tomcat.type..",有"..tomcat.eyes)
tomcat:catchMouse()

执行结果如下:

无标2题

posted @ 2026-05-12 18:11  NE_STOP  阅读(54)  评论(0)    收藏  举报