自由学习记录(24)

lua一些散杂疑惑

大G是增加可以通过字符串实现继承的功能

元表里面用别的表里的变量,如果自己这个表里没有这个变量,当然是用别人的,但如果自己的表里有了这个变量,就优先用自己的

如果 元表的self[调用的键] 查无此"变量",那会返回nil,不报错

Lua 复制代码
Object = {} -- 创建一个表 Object

function Object:new(o) -- 定义一个方法 new
    o = o or {}       -- 如果没有传入对象,则创建一个新表
    setmetatable(o, self) -- 设置新表的元表为 Object 本身
    self.__index = self   -- 确保未找到的字段从 Object 中查找
    return o              -- 返回新创建的对象
end

-- 使用 Object:new 创建一个实例
local instance = Object:new()
print(instance)           -- 输出一个 table

function Object:new() 并不是在创建一个变量叫 Object,而是为 Object 表定义了一个方法 new

在 Lua 中:

  • 函数本质上是一种值,就像数字、字符串或表一样。
  • 当函数作为一个表的字段时,这个函数就成为该表的一个"方法"。

一个名对应一个变量,函数或表都算一个变量

面向对象

在 Lua 中,面向对象(OOP)的概念是通过元表和函数来实现的。虽然 Lua 本身并没有内置的 OOP 机制,但它提供了足够灵活的元表和表结构,使得实现封装、继承和多态等 OOP 特性成为可能。

  • 封装:隐藏对象的内部实现,仅通过公开的接口访问数据。
  • 继承:一个对象可以继承另一个对象的属性和方法。
  • 多态:通过相同的接口实现不同的行为。

简单的对象和方法

Lua 复制代码
-- 定义类
local MyClass = {}
MyClass.__index = MyClass  -- 设置元表

-- 构造函数
function MyClass:new(value)
    local obj = setmetatable({}, self)  -- 创建对象并绑定元表
    obj.value = value or 0             -- 初始化属性
    return obj
end

-- 定义方法
function MyClass:getValue()
    return self.value
end

function MyClass:setValue(value)
    self.value = value
end

-- 使用类
local obj = MyClass:new(10)  -- 创建对象
print(obj:getValue())        -- 输出:10
obj:setValue(20)
print(obj:getValue())        -- 输出:20

封装私有属性

Lua 复制代码
local MyClass = {}

function MyClass:new(value)
    local obj = {}           -- 对象
    local privateValue = value or 0  -- 私有属性

    -- 公有方法
    function obj:getValue()
        return privateValue
    end

    function obj:setValue(value)
        privateValue = value
    end

    return obj
end

-- 使用类
local obj = MyClass:new(10)
print(obj:getValue())  -- 输出:10
obj:setValue(20)
print(obj:getValue())  -- 输出:20
-- print(obj.privateValue)  -- 错误:无法直接访问私有属性

还挺巧妙的,把函数变量声明在myclass的new方法里面,通过不local的全局可访问能力达到public的效果(但如果止步于此,等这个函数死了之后,里面的的变量还是会死,所以return obj),而闭包则可以让存了setvalue,getvalue这两个方法的obj返回,里面的单独设置的local变量,则被obj的两个方法形成了新的束缚,所以一直活在new方法里面,但是产生不了可以直接点出的联系

完整的一次封装

Lua 复制代码
-- 定义基类
local Animal = {}
Animal.__index = Animal  --如果 __index 是一个表,Lua 会在这个表中查找键
                         --如果 __index 是一个函数,Lua 会调用这个函数并将表和键作为参数传入
--通过 Animal.__index = Animal,我们让 Animal 表可以在对象的元表中作为 __index 的值。
--当对象无法找到某个键时,Lua 就会在 Animal 表中查找。

-- 构造函数
function Animal:new(name)
    local obj = setmetatable({}, self)
    obj.name = name or "Unnamed"
    return obj
end

-- 定义方法
function Animal:speak()
    print(self.name .. " makes a sound.")
end

---------------------------------------------------
-- 定义子类
local Dog = setmetatable({}, {__index = Animal})  -- Dog 继承自 Animal
Dog.__index = Dog

-- 构造函数(重写)
function Dog:new(name, breed)
    local obj = Animal.new(self, name)  -- 调用基类构造函数
    obj.breed = breed or "Unknown"
    return obj
end

-- 定义方法(重写)
function Dog:speak()
    print(self.name .. " barks.")
end

-- 定义方法(扩展)
function Dog:getBreed()
    return self.breed
end
---------------------------------------------------
-- 使用基类
local animal = Animal:new("Generic Animal")
animal:speak()  -- 输出:Generic Animal makes a sound.

-- 使用子类
local dog = Dog:new("Buddy", "Golden Retriever")
dog:speak()     -- 输出:Buddy barks.
print(dog:getBreed())  -- 输出:Golden Retriever

元表

元表的实际用途是模拟面向对象编程 ,使用元表来实现类的继承对象方法

重载操作:定义自定义的算术或比较操作符行为。

表的默认值:使用 __index 为表设置默认值。

元表是为表设计 的,不能直接为其他类型(如数字、字符串)设置元表

如果需要为字符串或数字定义行为,可以通过 元表代理 来实现。

使用元表时,请确保设计的行为符合预期,避免不必要的复杂性。

__call:定义表作为函数调用时的行为。

__tostring:定义表被 tostring 转换时的行为。

__len:定义 # 运算符的行为(获取长度)。

__index:控制表的"读取"操作。

如果访问的键不存在,则会调用元表中的 __index 方法。

__newindex:控制表的"写入"操作。

如果试图 向表中写入不存在的键,则触发 __newindex。

__index 是一张表时,Lua 会在这个表中查找访问的键。

Lua 复制代码
local defaults = {x = 10, y = 20}
local point = {}
setmetatable(point, {__index = defaults})

print(point.x)  -- 输出:10
print(point.z)  -- 输出:nil,因为 `defaults` 中没有 `z`

通过 __index,可以为表设置默认值。当键不存在时返回默认值,而不报错或返回 nil

Lua 复制代码
local defaults = {x = 0, y = 0}
local point = {}
setmetatable(point, {__index = defaults})

print(point.x)  -- 输出:0
print(point.y)  -- 输出:0

使用 __index 函数,可以在键访问时动态计算返回值。

Lua 复制代码
local myTable = {}
local mt = {
    __index = function(t, key)
        return "Dynamic value for key: " .. key
    end
}
setmetatable(myTable, mt)

print(myTable.abc)  -- 输出:Dynamic value for key: abc
print(myTable.xyz)  -- 输出:Dynamic value for key: xyz

通过 __index,可以模拟对象的继承关系。 同时,也可以代理访问

Lua 复制代码
-- 父类
local Parent = {name = "Parent"}
function Parent:sayHello()
    print("Hello from " .. self.name)
end

-- 子类
local Child = {name = "Child"}
setmetatable(Child, {__index = Parent})

Child:sayHello()  -- 输出:Hello from Child
Lua 复制代码
local source = {a = 1, b = 2}
local proxy = {}
setmetatable(proxy, {__index = source})

print(proxy.a)  -- 输出:1(从 `source` 中获取)
print(proxy.b)  -- 输出:2(从 `source` 中获取)

如果__index 是表,并且该表本身也有元表,Lua 会递归查找

__index的键如果在对象表里已经有了,

就不用在传入的那个表里找是否存在这个键,然后递归下去了

Lua 复制代码
local grandparent = {x = 1}
local parent = {}
setmetatable(parent, {__index = grandparent})
local child = {}
setmetatable(child, {__index = parent})

print(child.x)  -- 输出:1(从 `grandparent` 中获取)

rawget绕过元表,尝试获取对象表里的值,没有找到就返回nil

rawset则绕过元表,同理修改

Lua 复制代码
local defaults = {x = 10}
local point = {}
setmetatable(point, {__index = defaults})

print(point.x)        -- 输出:10
print(rawget(point, "x")) -- 输出:nil(`point` 本身没有 `x`)

一个综合的例子

Lua 复制代码
local defaults = {x = 0, y = 0}
local point = {}

local mt = {
    __index = function(t, key)
        -- 优先从默认值中查找
        if defaults[key] then
            return defaults[key]
        end
        -- 动态生成其他值
        return "Dynamic-" .. key
    end
}

setmetatable(point, mt)

print(point.x)  -- 输出:0(默认值)
print(point.y)  -- 输出:0(默认值)
print(point.z)  -- 输出:Dynamic-z

__eq:定义相等 (==)

__lt:定义小于 (<)

__le:定义小于等于 (<=)

Lua 复制代码
local t1 = {value = 10}
local t2 = {value = 20}

local mt = {
    __lt = function(a, b)
        return a.value < b.value
    end,
    __eq = function(a, b)
        return a.value == b.value
    end
}

setmetatable(t1, mt)
setmetatable(t2, mt)

print(t1 < t2)  -- 输出:true
print(t1 == t2) -- 输出:false

元表本质上是一张表,它可以定义一组元方法,用来控制对表的某些操作的行为

元表的核心机制是通过元方法来实现的,元方法是元表中的特定键(如 __add、__index 等)

setmetatable(table, metatable)

为 table 设置元表为 metatable

getmetatable(table)

获取一个表的元表,如果没有设置元表,则返回 nil

__add:定义加法 (+)

__sub:定义减法 (-)

__mul:定义乘法 (*)

__div:定义除法 (/)

__mod:定义取模 (%)

__pow:定义幂运算 (^)

Lua 复制代码
local t1 = {value = 10}
local t2 = {value = 20}

local mt = {
    __add = function(a, b)
        return {value = a.value + b.value}
    end
}

setmetatable(t1, mt)
setmetatable(t2, mt)

local result = t1 + t2
print(result.value)  -- 输出:30

表之间的加减乘除默认会报错

定义加号,需要传入两个参数,如果是要两个表里的age相加,那么里面必须要有age

运算符只有小于和小于等于,官方没给别的,要自己取反

--concat是..的重写

协程

wrap创造的协程不返回是否执行成功

协程的yield可以返回值,然后用多个变量去接收

第一个默认为是否执行成功true 或者false

在 Lua 中,coroutine.wrap 是用于创建协同程序的一种方式,它与 coroutine.create 类似,但有一些显著的区别和特点。

coroutine.wrap 接收一个函数,返回一个新的函数(不是协同程序)。当调用这个返回的函数时,实际上是启动或恢复对应的协同程序,并返回协同程序运行的结果。

协程的本质是一个线程对象

加了local的变量不会存到大G表里

require()

在 Lua 中,require 是一个用于++加载和运行模块的函数++ ,广泛用于组织代码和++实现模块化开发++ 。以下是关于 require 的详细讲解:


1. 基本作用

require 用于加载 Lua 脚本或 C 模块,并++执行加载的代码++ 。如果模块++已经加载过一次++ ,require 会直接++返回加载的结果++ ,而++不会重复加载++。

-- example.lua 文件

local M = {}

M.greet = function()

print("Hello, Lua!")

end
return M

-- 主脚本 main.lua
local example = require("example")

example.greet() -- 输出:Hello, Lua!

  • require("example") 加载 example.lua 文件。
  • 加载成功后返回文件中 return 的值。

2. 加载机制

require 的加载行为由 Lua 的 ++package.pathpackage.cpath 决定++。它们定义了 Lua 和 C 模块的搜索路径:

  • package.path : 用于搜索 Lua 文件的路径,例如++.lua.luac++文件。
  • package.cpath : 用于搜索 C 模块的路径,例如++动态链接库(.dll.so 等)++。
搜索路径示例
复制代码
print(package.path)
-- 输出类似:
-- ./?.lua;./?/init.lua;/usr/local/share/lua/5.1/?.lua;...

print(package.cpath)
-- 输出类似:
-- ./?.so;/usr/local/lib/lua/5.1/?.so;...

说明

  • ? 是占位符,会被替换为模块名。例如,require("example") 会尝试加载 ./example.lua./example/init.lua
  • 如果路径中找不到模块,会报错:module 'example' not found

3. 避免重复加载

Lua 内部维护了一个全局表 package.loaded ,用来记录已经加载的模块。每次调用 require 时,Lua 会先检查这个表,只有++模块尚未加载时才会重新加载++。

复制代码
local module1 = require("example")
local module2 = require("example")

-- module1 和 module2 是同一个表对象
print(module1 == module2) -- 输出:true

4. 模块结构

为了更好地组织模块,++Lua 社区约定了标准的模块结构++。一个模块通常:

  • 定义为一个表。
  • 包含需要暴露的功能。
  • 在文件末尾++使用 return 返回这个表++。
示例:简单模块
复制代码
-- mymodule.lua
local M = {}

M.add = function(a, b)
    return a + b
end

M.sub = function(a, b)
    return a - b
end

return M

-- main.lua
local mymodule = require("mymodule")

print(mymodule.add(5, 3)) -- 输出:8

print(mymodule.sub(5, 3)) -- 输出:2


5. 加载非 Lua 文件

require 也可以加载 C 模块,通常是通过动态链接库实现的。例如,加载一个 example.so 文件:

复制代码
local example = require("example")  -- 会从 package.cpath 搜索路径加载

需要注意的是,C 模块需要定义特定的入口点,以便 Lua 调用。


6. 自定义 require 的搜索路径

可以通过修改 package.pathpackage.cpath 来自定义 require 的搜索路径。例如:

复制代码
package.path = package.path .. ";./custom/?.lua"

local customModule = require("mymodule")

上述代码会让 require 搜索 ./custom/mymodule.lua


7. 动态加载模块

如果只想加载模块而不缓存,可以直接使用 loadfiledofile

  • dofile: 加载并立即运行脚本。
复制代码
dofile("example.lua") -- 执行 example.lua 中的代码

local chunk = ++loadfile("example.lua")++

chunk() -- 执行加载的代码块

相比之下,++require 更安全且高效,因为它具有模块缓存机制++。


8. requiremodule 的历史

Lua 5.1 曾经使用 module 函数来定义模块,但后来在 Lua 5.2 开始逐步弃用。现代 Lua 模块通常直接用表返回。


总结

  • require 是 ++Lua 中模块化编程的核心工具++。
  • 它++通过 package.pathpackage.cpath 实现模块搜索。++
  • 加载的模块++会被缓存到 package.loaded 中++,避免重复加载。
  • ++现代 Lua 使用 return 的表来组织模块内容。++

print(d)结果为nil

相关推荐
Chef_Chen2 小时前
从0开始学习R语言--Day18--分类变量关联性检验
学习
键盘敲没电2 小时前
【IOS】GCD学习
学习·ios·objective-c·xcode
海的诗篇_3 小时前
前端开发面试题总结-JavaScript篇(一)
开发语言·前端·javascript·学习·面试
AgilityBaby3 小时前
UE5 2D角色PaperZD插件动画状态机学习笔记
笔记·学习·ue5
AgilityBaby3 小时前
UE5 创建2D角色帧动画学习笔记
笔记·学习·ue5
武昌库里写JAVA5 小时前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
一弓虽6 小时前
git 学习
git·学习
Moonnnn.8 小时前
【单片机期末】串行口循环缓冲区发送
笔记·单片机·嵌入式硬件·学习
修电脑的猫8 小时前
带有输入的CDS和程序调用
开发语言·lua
viperrrrrrrrrr78 小时前
大数据学习(131)-Hive数据分析函数总结
大数据·hive·学习