C++和Lua交互总结
C++和Lua交互总结
- Chapter1. C++和Lua交互总结
- 一、Lua与C++的交互机制——Lua堆栈
- 二、堆栈的操作
- 三、C++ 调用 Lua
- 1)C++获取Lua值
- 2)C++调用Lua函数
- 示例:
- 四、Lua 调用 C++
- 包装C++函数
- 最后总结一下
Chapter1. C++和Lua交互总结
原文链接:https://blog.csdn.net/qq826364410/article/details/88624824/
一、Lua与C++的交互机制——Lua堆栈
Lua和C++ 的交互机制的基础在于Lua提供了一个虚拟栈,C++ 和Lua之间的所有类型的数据交换都通过这个栈完成。无论何时C++想从Lua中调用一个值,被请求的值将会被压入栈,无论何时C++想要传递一个值给Lua,首先将整个值压栈,然后就可以在Lua中调用。
Lua中,对虚拟栈提供正向索引和反向索引两种索引方式,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶。重要!后面所有的交互,都是基于Lua的虚拟栈来通信。假设当前Lua的栈中有5个元素,如下图所示:
二、堆栈的操作
因为lua与c/c++是通过栈来通信,lua提供了C API对栈进行操作。
我们先来看一个最简单的例子:
#include <iostream>
#include <string.h>
using namespace std;extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}int main()
{//1.创建一个state // luaL_newstate返回一个指向堆栈的指针lua_State *L = luaL_newstate();//2.入栈操作 lua_pushstring(L, "I am so cool~");lua_pushnumber(L, 20);//3.取值操作 if (lua_isstring(L, 1)) { //判断是否可以转为string cout << lua_tostring(L, 1) << endl; //转为string并返回 }if (lua_isnumber(L, 2)) {cout << lua_tonumber(L, 2) << endl;}//4.关闭state lua_close(L);getchar();return 0;
}
在执行这个例子之前,我们需要引入Lua.lib静态库,也就是上文中extern "C"中执行的include。
extern “C”
主要作用就是为了能够正确实现C++ 代码调用其他C语言代码。加上extern “C”后,会指示编译器这部分代码按c语言的进行编译,而不是C++ 的。由于C++ 支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名,比如_Log_int_int;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名,比如_Log。
void Log(int a, int b){}
Lua源代码的下载地址: http://www.lua.org/ftp/,但没有提供相应的静态库,只有.c和.h文件,需要我们自己生成静态库。
生成Lua静态库方法: https://blog.csdn.net/qq826364410/article/details/88563408
也可以直接下载我生成好的静态库:https://download.csdn.net/download/qq826364410/11029611
好了,我们有了Lua静态库,就可以开心的玩耍了。
引入Lua静态库
- 首先,新建一个Visual C++的空项目,右键点击工程属性,选择VC++目录,
- 把lua工程中的.h头文件所在的目录加到包含目录中
- 把Lua静态库文件所在的目录加到库目录中,
- 最后,点击链接器->输入->附加依赖项->加上生成的Lua静态库,比如Lua.lib,记得用分号";"与其他库隔开。
OK,大功告成!
Lua虚拟栈在源码中是如何实现的:
Lua栈是在创建lua_State时建立的,TValue stack[max_stack_len] ,欲知内情可以查 Lua源码lstate.c的stack_init函数
Lua栈可以存储数字,字符串,表,闭包等,它们最终都用TValue这种数据结构来保存 。
TValue结构对应于lua中的所有数据类型, 是一个{值, 类型} 结构, 这就lua中动态类型的实现, 它把值和类型绑在一起, 用tt记录value的类型, value是一个联合结构, 由Value定义, 可以看到这个联合有四个域, 先说明简单的
p – 可以存一个指针, 实际上是lua中的light userdata结构
n – 所有的数值存在这里, 不管是int , 还是float
b – Boolean值存在这里, 注意, lua_pushinteger不是存在这里, 而是存在n中, b只存布尔
gc – 其他诸如table, thread, closure, string需要内存管理垃圾回收的类型都存在这里
gc是一个指针, 它可以指向的类型由联合体GCObject定义, 从图中可以看出, 有string, userdata, closure, table, proto, upvalue, thread
可以的得出如下结论:
1. lua中, number, boolean, nil, light userdata四种类型的值是直接存在栈上元素里的, 和垃圾回收无关。
2. lua中, string, table, closure, userdata, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收。
三、C++ 调用 Lua
C++ 可以获取Lua中的值,可以调用Lua函数,还可以修改Lua文件
1)C++获取Lua值
使用lua_getglocal来获取值,然后将其压栈
使用lua_toXXX将栈中元素取出转成相应的C++类型的值
如果Lua值为table类型的话,通过lua_getfield和lua_setfield获取和修改表中元素的值
2)C++调用Lua函数
使用lua_getglobal来获取函数,然后将其压入栈;
如果这个函数有参数的话,就需要依次将函数的参数也压入栈;
这些准备工作都准备就绪以后,就调用lua_pcall开始调用函数了,调用完成以后,会将返回值压入栈中;
示例:
新建一个简单的lua放到工程的同级目录下
hello.lua
str = "I am x-man."
tbl = {name = "DC", id = 20114442}
function add(a,b)return a + b
end
然后,我们写一个Lua1.cpp来访问lua中的数据
#include <iostream>
#include <string.h>
using namespace std;extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
int main()
{//1.创建Lua状态,返回一个指向堆栈的指针lua_State *L = luaL_newstate();if (L == NULL){return;}//2.加载lua文件int bRet = luaL_loadfile(L, "hello.lua");if (bRet){cout << "load file error" << endl;return;}//3.运行lua文件bRet = lua_pcall(L, 0, 0, 0);if (bRet){cout << "pcall error" << endl;return;}//4.读取全局变量,// 1.把 str 压栈 2.由lua去寻找全局变量str的值,并将str的值返回栈顶(替换str)// 如果存在相同命名的其他变量、table或函数,就会报错(读取位置发生访问冲突)lua_getglobal(L, "str");// -1取出栈顶元素,转化为stringstring str = lua_tostring(L, -1);cout << "str = " << str.c_str() << endl;//5.读取table,把table压栈lua_getglobal(L, "tbl");//-------------------------------// 1.把name压入栈中,2.由lua去寻找table中name键的值,并将键值返回栈顶(替换name)// 相当于lua_pushstring(L, "name") + lua_gettable(L, -2)执行结果是一样的lua_getfield(L, -1, "name");// 把name压入栈中//lua_pushstring(L, "name");// 弹出栈上的name,并从表中找到name的键值,把结果放在栈上相同的位置//lua_gettable(L, -2);//---------------------------------str = lua_tostring(L, -1);// 因为table在栈顶的下面,所以取-2,把id压栈,由lua找到table中id键的值,并返回栈顶(替换id)lua_getfield(L, -2, "id");// id的值已经在栈顶,取-1int id = lua_tonumber(L, -1);cout << "tbl:name = " << str.c_str() << endl;cout << "tbl:id = " << id << endl;// 读取函数,// 1.将函数add放入栈中,2.由lua去寻找函数add,并将函数add返回栈顶(替换add)。lua_getglobal(L, "add"); // 获取函数,压入栈中lua_pushnumber(L, 10); // 压入第一个参数lua_pushnumber(L, 20); // 压入第二个参数// 栈过程:参数出栈->保存参数->参数出栈->保存参数->函数出栈->调用函数->返回结果入栈// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。int iRet = lua_pcall(L, 2, 1, 0);if (iRet) {// 调用出错const char *pErrorMsg = lua_tostring(L, -1);cout << pErrorMsg << endl;lua_close(L);return;}if (lua_isnumber(L, -1)) //取值输出{int fValue = lua_tonumber(L, -1);cout << "Result is " << fValue << endl;}// 栈的索引方式可以是正数也可以是负数,区别是:1永远表示栈底元素,-1永远表示栈顶元素。//至此,栈中的情况是://=================== 栈顶 =================== // 索引 类型 值// 5或-1 int 30 // 4或-2 int 20114442// 3或-3 string shun // 2或-4 table tbl// 1或-5 string I am so cool~//=================== 栈底 =================== lua_pushstring(L, "Master");// 会将"Master"值出栈,保存值,找到到table的name键,如果键存在,存储到name键中lua_setfield(L, 2, "name");// 读取lua_getfield(L, 2, "name");str = lua_tostring(L, -1);cout << "tbl:name = " << str.c_str() << endl;// 创建新的tablelua_newtable(L);lua_pushstring(L, "A New Girlfriend");lua_setfield(L, -2, "name");// 读取lua_getfield(L, -1, "name");str = lua_tostring(L, -1);cout << "newtbl:name = " << str.c_str() << endl;//7.关闭state// 销毁指定 Lua 状态机中的所有对象, 并且释放状态机中使用的所有动态内存。// (如果有垃圾收集相关的元方法的话,会调用它们)lua_close(L);getchar();return 0;
}
代码中,已经有很详细的注释了,这里总结一下:
1. 读取lua的全局变量:
lua_getglobal(L, "str");
内部实现:1.把全局变量 str 里的值压栈 2.由lua去寻找全局变量str的值,并将str的值返回栈顶(替换str)
注意:如果存在相同命名的其他变量、table或函数,就会报错(读取位置发生访问冲突)
2. 读取table中的键值:
lua_getglobal(L, "tbl");
lua_getfield(L, -1, "name");
lua_getglobal方法跟上面的实现是一样的。
lua_getfield方法:
内部实现:1.把name压入栈中,2.由lua去寻找table中name键的值,如果键存在,将键值返回栈顶(替换name)
注意:这里的参数-1,就是表示把table中的键值返回到栈顶。
- 调用lua中的函数:
lua_getglobal(L, "add"); // 获取函数,压入栈中
lua_pushnumber(L, 10); // 压入第一个参数
lua_pushnumber(L, 20); // 压入第二个参数
// 栈过程:参数出栈->保存参数->参数出栈->保存参数->函数出栈->调用函数->返回结果入栈
// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数,
// iRet为0表示调用成功
int iRet = lua_pcall(L, 2, 1, 0);
四、Lua 调用 C++
Lua可以调用由C++定义、实现具体的函数
步骤:
- 将C++的函数包装成Lua环境认可的Lua_CFunction格式
- 将包装好的函数注册到Lua环境中
- 像使用普通Lua函数那样使用注册函数
包装C++函数
为了从Lua脚本中调用C++函数,需要将被调用的C++函数从普通的C++函数包装成Lua_CFunction格式,并需要在函数中将返回值压入栈中,并返回返回值个数。
int (*lua_CFunction) (lua_State *L);
例如有一个C++函数:
int add(int a,int b)
{return a+b;
}
包装为:
int add(lua_state *L)
{int a = lua_tonumber(-1);int b = lua_tonumber(-2);int sum = a+b;// 将返回值压入栈中lua_pushnumber(L,sum);// 返回返回值个数return 1;
}
示例:
新建一个简单的lua放到工程的同级目录下
avg.lua
avg, sum = average(10, 20, 30, 40, 50)
print("The average is ", avg)
print("The sum is ", sum)print("age", age)
for k,v in pairs(newTable) do print("k = ",k," v = ",v)
end
print("name", newTable.name)
然后,创建一个Lua2.cpp:
#include <stdio.h>extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}static int average(lua_State *L)
{/* 得到参数个数 */int n = lua_gettop(L);double sum = 0;int i;/* 循环求参数之和 */for (i = 1; i <= n; i++){/* 求和 */sum += lua_tonumber(L, i);}/* 压入平均值 */lua_pushnumber(L, sum / n);/* 压入和 */lua_pushnumber(L, sum);/* 返回返回值的个数 */return 2;
}int main(int argc, char *argv[])
{/* 初始化Lua *//* 指向Lua解释器的指针 */lua_State* L = luaL_newstate();/* 载入Lua基本库 */luaL_openlibs(L);/* 注册函数 */lua_register(L, "average", average);// 设置lua中的全局变量lua_pushinteger(L, 18); //入栈lua_setglobal(L, "age"); //1.先将18值出栈,保存值,2.在lua中,把值存储到全局变量age中// 设置lua中tablelua_newtable(L); //创建一张空表,并将其压栈lua_pushstring(L, "lili");// 入栈// 1.先将"lili"值出栈,保存值,2.找table的name键,如果键存在,存储到name键中lua_setfield(L, -2, "name");//栈顶是lili,新创建的table在lili下,所以是-2// 将table赋值到lua中,并弹出tablelua_setglobal(L, "newTable");/* 运行脚本 */luaL_dofile(L, "avg.lua");/* 清除Lua */lua_close(L);/* 暂停 */printf("Press enter to exit…");getchar();return 0;
}
1. 读取C++的变量:
// 设置lua中的全局变量
lua_pushinteger(L, 18); //入栈
lua_setglobal(L, "age");
lua_setglobal(L, “age”) 内部实现: 1.先将值出栈,保存值,2.在lua中,把值存储到全局变量age中
2. 调用在C++中创建的新表的元素:
// 设置lua中table
lua_newtable(L); //创建一张空表,并将其压栈
lua_pushstring(L, "lili");// 入栈
// 1.先将"lili"值出栈,保存值,2.找table的name键,如果键存在,存储到name键中
lua_setfield(L, -2, "name");//栈顶是lili,新创建的table在lili下,所以是-2
// 将table赋值到lua中,并弹出table
lua_setglobal(L, "newTable");
lua_setglobal(L, “newTable”) 内部实现: 1.先将table出栈,保存table,2.在lua中,存储到newTable表中
在lua中,print(“name”, newTable.name),使用newTable.name调用在C++中创建的新表的元素。
3. 调用C++中的函数:
将C++的函数包装成Lua环境认可的Lua_CFunction格式
将包装好的函数注册到Lua环境中
像使用普通Lua函数那样使用注册函数
包装C++函数:
static int average(lua_State *L)
{/* 得到参数个数 */int n = lua_gettop(L);double sum = 0;int i;/* 循环求参数之和 */for (i = 1; i <= n; i++){/* 求和 */sum += lua_tonumber(L, i);}/* 压入平均值 */lua_pushnumber(L, sum / n);/* 压入和 */lua_pushnumber(L, sum);/* 返回返回值的个数 */return 2;
}
将包装好的函数注册到Lua环境中
/* 注册函数 */
lua_register(L, "average", average);
在lua中正常调用
avg, sum = average(10, 20, 30, 40, 50)
4.把C++的函数封装成模块
把C++的函数封装成模块:
①将C++的函数包装成Lua环境认可的Lua_CFunction格式,调用luaL_newlib,放入到一个lua表中压入栈里。
②将自定义模块,注册到Lua环境中。
③在lua中,加上自定义模块名调用C++函数。
avg.lua,这里的lua文件,在调用C++的函数时,需要加上自定义的模块名。 比如,我们定义模块名为mylib。
avg, sum = mylib.average(10, 20, 30, 40, 50)
print("The average is ", avg)
print("The sum is ", sum)
Lua1.cpp
#include <stdio.h>extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}static int average(lua_State *L)
{/* 得到参数个数 */int n = lua_gettop(L);double sum = 0;int i;/* 循环求参数之和 */for (i = 1; i <= n; i++){/* 求和 */sum += lua_tonumber(L, i);}/* 压入平均值 */lua_pushnumber(L, sum / n);/* 压入和 */lua_pushnumber(L, sum);/* 返回返回值的个数 */return 2;
}// 1. 列出需要封装的C++函数
// luaL_Reg为注册函数的数组类型
static const luaL_Reg mylibs_funcs[] = {{ "average", average },{ NULL, NULL }
};// 2. 将所有函数放到一个table中,并压入栈中
int lua_openmylib(lua_State* L) {//创建一个新的表,将所有函数放到一个table中//将这个table压到stack里luaL_newlib(L, mylibs_funcs);return 1;
}// 3. 将自定义模块加到注册列表里
static const luaL_Reg lua_reg_libs[] = {{ "base", luaopen_base },{ "mylib", lua_openmylib }, //这里为自定义的模块名字mylib{ NULL, NULL }
};int main(int argc, char *argv[])
{/* 初始化Lua *//* 指向Lua解释器的指针 */lua_State* L = luaL_newstate();/* 载入Lua基本库 */luaL_openlibs(L);//4. 注册让lua使用的模块const luaL_Reg* lua_reg = lua_reg_libs;for (; lua_reg->func; ++lua_reg) {// 加载模块// 首先查找 package.loaded 表, 检测 modname 是否被加载过。 // 如果被加载过,require 返回 package.loaded[modname] 中保存的值。// 如果 modname 不在 package.loaded 中, 则调用函数 openf ,并传入字符串 modname。// 将其返回值置入 package.loaded[modname]。// 如果最后一个参数为真, 同时也将模块设到全局变量 modname 里。在栈上留下该模块的副本。luaL_requiref(L, lua_reg->name, lua_reg->func, 1);// 从栈中弹出 1 个元素lua_pop(L, 1);}/* 运行脚本 */luaL_dofile(L, "avg.lua");/* 清除Lua */lua_close(L);/* 暂停 */printf("Press enter to exit…");getchar();return 0;
}
5. Lua调用C++类注册生成的Lua模块
由于篇幅的限制,请移步:https://blog.csdn.net/qq826364410/article/details/88652441
6. 在Lua中以面向对象的方式使用C++注册的类
由于篇幅的限制,请移步:https://blog.csdn.net/qq826364410/article/details/88639408
Lua和C++交互:全局数组交互
https://blog.csdn.net/qq826364410/article/details/88713839
补充
这里补充其他一些栈操作:
int lua_gettop (lua_State *L); //返回栈顶索引(即栈长度)
// lua_settop将栈顶设置为一个指定的位置,即修改栈中元素的数量。
// 如果值比原栈顶高,则高的部分nil补足,如果值比原栈低,则原栈高出的部分舍弃。
// 所以可以用lua_settop(0)来清空栈。
void lua_settop (lua_State *L, int idx);
void lua_pushvalue (lua_State *L, int idx); //将idx索引上的值的副本压入栈顶
void lua_remove (lua_State *L, int idx); //移除idx索引上的值
void lua_insert (lua_State *L, int idx); //弹出栈顶元素,并插入索引idx位置
void lua_replace (lua_State *L, int idx); //弹出栈顶元素,并替换索引idx位置的值
// 确保堆栈上至少有 n 个额外空位。 如果不能把堆栈扩展到相应的尺寸,
// 函数返回假。 失败的原因包括将把栈扩展到比固定最大尺寸还大 (至少是几
// 千个元素)或分配内存失败。 这个函数永远不会缩小堆栈; 如果堆栈已经
// 比需要的大了,那么就保持原样
int lua_checkstack (lua_State *L, int n);
下面就分两个主要部分进行介绍(C++和栈操作;以及Lua和栈操作)
2.C++和栈之间操作相关函数
①c -> stack 将C++数据压到栈里函数:lua_pushxxx
LUA_API void (lua_pushnil) (lua_State *L);
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void (lua_pushboolean) (lua_State *L, int b);
LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int (lua_pushthread) (lua_State *L);
②stack -> c 判断栈里类型相关函数: lua_isxxx(lua_State *L, int idx)
LUA_API int (lua_isnumber) (lua_State *L, int idx);
LUA_API int (lua_isstring) (lua_State *L, int idx);
LUA_API int (lua_iscfunction) (lua_State *L, int idx);
LUA_API int (lua_isinteger) (lua_State *L, int idx);
LUA_API int (lua_isuserdata) (lua_State *L, int idx);
LUA_API int (lua_type) (lua_State *L, int idx);
③stack -> c 获取栈里数据相关函数:lua_toxxx (lua_State *L, int idx)
LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum);
LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum);
LUA_API int (lua_toboolean) (lua_State *L, int idx);
LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
LUA_API size_t (lua_rawlen) (lua_State *L, int idx);
LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
LUA_API void *(lua_touserdata) (lua_State *L, int idx);
LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
LUA_API const void *(lua_topointer) (lua_State *L, int idx);
3.Lua和栈之间的操作相关函数
①从Lua中得到数据放到栈里进行操作:lua_getxxx
LUA_API int (lua_getglobal) (lua_State *L, const char *name);
LUA_API int (lua_gettable) (lua_State *L, int idx);
LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawget) (lua_State *L, int idx);
LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
LUA_API int (lua_getuservalue) (lua_State *L, int idx);
②从栈里将数据写入到Lua中:lua_setxxx
LUA_API void (lua_setglobal) (lua_State *L, const char *name);
LUA_API void (lua_settable) (lua_State *L, int idx);
LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n);
LUA_API void (lua_rawset) (lua_State *L, int idx);
LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p);
LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
LUA_API void (lua_setuservalue) (lua_State *L, int idx);
最后总结一下
-
Lua和C++是通过一个虚拟栈来交互通信的。
-
C++调用Lua: 由C++先把函数名、变量名、table中键放入栈中,然后把函数名、变量名、table中键出栈,并返回对应的值到栈顶,再由栈顶返回C++。
-
Lua调C++:
**Lua调用C++的函数:**先把普通的C++函数包装成Lua_CFunction格式,然后注册函数到Lua解释器中,然后由Lua去调用这个模块的函数。
**Lua以面向对象的方式调用C++的类:**新建一个元表metatable,并设置元表里key为"__index"的值的为metatable本身,然后将成员操作方法添加到元表metatable里,在创建对象函数中,把元表赋值给对象指针,这样通过":"操作符就可以找到对应的方法了。
相关文章:

C++和Lua交互总结
C和Lua交互总结 Chapter1. C和Lua交互总结一、Lua与C的交互机制——Lua堆栈二、堆栈的操作三、C 调用 Lua1)C获取Lua值2)C调用Lua函数示例: 四、Lua 调用 C包装C函数 最后总结一下 Chapter1. C和Lua交互总结 原文链接:https://bl…...

nvm安装和切换node版本
1、nvm list查看已安装的node版本 2、查看当前使用的npm和node版本 3、安装某版本的node 4、 切换node版本...

每日一题8.2 2536
2536. 子矩阵元素加 1 给你一个正整数 n ,表示最初有一个 n x n 、下标从 0 开始的整数矩阵 mat ,矩阵中填满了 0 。 另给你一个二维整数数组 query 。针对每个查询 query[i] [row1i, col1i, row2i, col2i] ,请你执行下述操作:…...

适配器模式(Adapter)
适配器模式用于将一个接口转换成用户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。 Adapter is a structural design pattern that…...

Spring学习笔记——1
Spring学习笔记——1 一、Spring入门1.1、学习路线1.2、传统Javaweb开发困惑及解决方法1.3、三种思想的提出和框架概念1.3.1、IoC、DI和AOP思想提出1.3.2、框架的基本特点 1.4、Spring概述1.5、BeanFactory快速入门1.6、ApplicationContext快速入门1.7、BeanFactory与Applicati…...

leetcode 406. 根据身高重建队列
2023.8.2 这题一开始有点让人懵逼的是有两个维度,一个是身高,还一个是前面人高于自己的人数。这种题一般需要先固定一个维度,再去确定另外一个维度,不要想着兼顾。 经过纸上模拟,我的思路是先通过身高进行从大到小排序…...
Matlab实现AGNES算法
在数据分析和机器学习中,聚类是一种常用的无监督学习方法,它可以将数据点按照某种相似度标准进行分组,从而发现数据中的结构和模式。聚类算法有很多种,其中一种比较经典的是AGNES算法,它是一种基于层次的聚类算法&…...

STM32F4_外部SRAM
目录 前言 1. SRAM控制原理 1.1 SRAM功能框图 1.2 SRAM读写时序 2. FSMC简介 2.1 FSMC架构 2.2 FSMC地址映射 2.3 FSMC控制SRAM时序 3. FSMC结构体 4. 库函数配置FSMC 5. 实验程序 5.1 main.c 5.2 SRAM.c 5.3 SRAM.h 前言 STM32F4自带了192K字节的SRAM࿱…...

Java的代理模式
java有三种代理模式 静态代理 jdk动态代理 cglib实现动态代理 代理模式的定义: 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的…...
FilterAttributeOnClassMethod
目录 1 BadMethodFilterAttribute 2 FilterAttributeOnClassMethod 2.1 OnMethodExecuted 2.2 OnMethodExecutedAsync 2.3 OnMethodExecuting BadMethodFilterAttribute using System; using System.Threading.Tasks; namespace Flatwhite.Core.Tests.Attributes …...
springboot + (mysql/pgsql) + jpa 多数据源(不同类数据源)
配置文件: spring:datasource:primary:jdbc-url: jdbc:mysql://host:3306/数据库?useUnicodetrue&characterEncodingUTF-8&autoReconnecttrue&failOverReadOnlyfalse&serverTimezoneAsia/Shanghai&zeroDateTimeBehaviorconvertToNullusername…...
【Golang】Golang进阶系列教程--Go 语言 context 都能做什么?
文章目录 前言核心是 Context 接口:包含四个方法:遵循规则WithCancelWithDeadlineWithTimeoutWithValue 前言 很多 Go 项目的源码,在读的过程中会发现一个很常见的参数 ctx,而且基本都是作为函数的第一个参数。 为什么要这么写呢…...

画图干货!14种uml图类型及示例
1. 什么是 UML UML 是统一建模语言的缩写。UML 图是基于 UML(统一建模语言)的图表,目的是直观地表示系统及其主要参与者、角色、动作、工件或类,以便更好地理解、更改、维护或记录信息关于系统。简而言之,UML 是一种…...

计算机视觉实验:人脸识别系统设计
实验内容 设计计算机视觉目标识别系统,与实际应用有关(建议:最终展示形式为带界面可运行的系统),以下内容选择其中一个做。 1. 人脸识别系统设计 (1) 人脸识别系统设计(必做):根据…...

振弦采集仪完整链条的岩土工程隧道安全监测
振弦采集仪完整链条的岩土工程隧道安全监测 隧道工程是一种特殊的地下工程,其建设过程及运行期间,都受到各种内外力的作用,如水压、地震、地质变形、交通荷载等,这些因素都会对隧道的安全性产生影响。因此,对隧道的安…...

NLP实战9:Transformer实战-单词预测
目录 一、定义模型 二、加载数据集 三、初始化实例 四、训练模型 五、评估模型 🍨 本文为[🔗365天深度学习训练营]内部限免文章(版权归 *K同学啊* 所有) 🍖 作者:[K同学啊] 模型结构图: &a…...
使用Vue.js和Rust构建高性能的物联网应用
物联网(IoT)应用是现代技术的重要组成部分,它们可以在各种场景中(例如智能家居,工业自动化等)提供无缝的自动化解决方案。在这篇文章中,我们将探讨如何使用Vue.js和Rust构建高性能的物联网应用。 1. 为什么选择Vue.js…...

idea调节文字大小、日志颜色、git改动信息
idea调节菜单栏文字大小: 调节代码文字大小: 按住ctrl滚动滑轮可以调节代码文字大小: 单击文件即可在主窗口上打开显示: idea在控制台对不同级别的日志打印不同颜色 : “grep console”插件 点击某一行的时候&#x…...
避免大龄程序员边缘化:如何在技术行业中保持竞争力
目录 导语持续学习和进修维护专业形象寻找适合自己的领域构建个人品牌和网络拥抱变化和创新实例结语: 导语 导语:随着科技的不断发展,技术行业的竞争日益激烈。对于那些年龄稍长的程序员来说,如何保持竞争力并避免边缘化成为了一…...

Jenkins工具系列 —— 启动 Jenkins 服务报错
错误显示 apt-get 安装 Jenkins 后,自动启动 Jenkins 服务报错。 排查原因 直接运行jenkins命令 发现具体报错log:Failed to start Jetty或Failed to bind to 0.0.0.0/0.0.0.0:8080或Address already in use 说明:这里提示的是8080端口号…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
鸿蒙HarmonyOS 5军旗小游戏实现指南
1. 项目概述 本军旗小游戏基于鸿蒙HarmonyOS 5开发,采用DevEco Studio实现,包含完整的游戏逻辑和UI界面。 2. 项目结构 /src/main/java/com/example/militarychess/├── MainAbilitySlice.java // 主界面├── GameView.java // 游戏核…...