Golua——github.com/yuin/gopher-lua

目录

  • go调用lua
  • 安装
  • 使用
    • 注册表
    • 调用栈
    • Data model
    • Go中调用lua
  • API
    • Lua调用go
    • 打开Lua内置模块的子集
    • 使用Go创建模块
      • 示例1(官方)
      • 示例2
    • 关闭一个运行的lua虚拟机
    • 虚拟机之间共享lua字节码
  • go-lua调优
    • 预编译
    • 虚拟机实例池
    • 模块调用

go调用lua

这里比较下两个比较有名的go-lua包:

github.com/Shopify/go-luagithub.com/yuin/gopher-lua是两个Go语言库,允许Go程序与Lua脚本进行交互。

以下是这两个库之间的主要区别:

  1. Shopify/go-lua:

    • Shopify/go-lua是一个用Go编写的Lua解释器。
    • 它旨在提供一个轻量级、易于使用的Go和Lua之间的接口。
    • 该项目主要关注简单性和易集成性。
    • 它提供了调用Go函数和Lua函数之间的绑定。
  2. yuin/gopher-lua:

    • yuin/gopher-lua是一个更丰富功能且活跃维护的Go语言Lua虚拟机实现。
    • 它支持广泛的Lua功能,并包含一个标准库,涵盖许多常见的Lua功能。
    • 这个库允许Go代码执行Lua脚本,访问Lua变量,并注册Go函数供Lua脚本使用。
    • 它提供了更全面的文档和更大的社区,相比之下优于github.com/Shopify/go-lua
    • 根据我上次更新的信息,yuin/gopher-lua已经有了更近期的更新和改进,这表明它的维护活跃性更高。

在选择这些库时,请考虑项目的具体要求。如果您需要与Lua进行简单轻量级集成,github.com/Shopify/go-lua可能已经足够。另一方面,如果您需要一个功能更丰富且持续维护的库,支持更广泛的Lua功能,github.com/yuin/gopher-lua会是一个更好的选择。

安装

GopherLua supports >= Go1.9.

go get github.com/yuin/gopher-lua

使用

GopherLua的API与Lua的运行方式非常相似,但是堆栈仅用于传递参数和接收返回值。

Run scripts in the VM.

L := lua.NewState()
defer L.Close()
// 直接执行lua代码
if err := L.DoString(`print("hello")`); err != nil {panic(err)
}
L := lua.NewState()
defer L.Close()
// 执行lua脚本文件
if err := L.DoFile("hello.lua"); err != nil {panic(err)
}

调用栈和注册表大小:

LState的调用栈大小控制脚本中Lua函数的最大调用深度(Go函数调用不计入其中)。

LState的注册表实现对调用函数(包括Lua和Go函数)的栈式存储,并用于表达式中的临时变量。其存储需求会随着调用栈的使用和代码复杂性的增加而增加。

注册表和调用栈都可以设置为固定大小或自动大小。

当您在进程中实例化大量的LState时,值得花时间来调整注册表和调用栈的选项。

注册表

注册表可以在每个LState的基础上配置初始大小、最大大小和步长大小。这将允许注册表根据需要进行扩展。一旦扩展,它将不会再缩小。

 L := lua.NewState(lua.Options{RegistrySize: 1024 * 20,         // this is the initial size of the registryRegistryMaxSize: 1024 * 80,      // this is the maximum size that the registry can grow to. If set to `0` (the default) then the registry will not auto growRegistryGrowStep: 32,            // this is how much to step up the registry by each time it runs out of space. The default is `32`.})
defer L.Close()

如果注册表对于给定的脚本来说太小,最终可能会导致程序崩溃。而如果注册表太大,将会浪费内存(如果实例化了许多LState,这可能是一个显著的问题)。自动增长的注册表在调整大小时会带来一点点性能损耗,但不会影响其他方面的性能。

调用栈

调用栈可以以两种不同的模式运行,即固定大小或自动大小。固定大小的调用栈具有最高的性能,并且具有固定的内存开销。自动大小的调用栈将根据需要分配和释放调用栈页面,从而确保任何时候使用的内存量最小。缺点是,每次分配新的调用帧页面时都会带来一点小的性能影响。默认情况下,一个LState会以每页8个调用帧的方式分配和释放调用栈帧,因此不会在每个函数调用时产生额外的分配开销。对于大多数用例,自动调整大小的调用栈的性能影响可能是可以忽略的。

 L := lua.NewState(lua.Options{CallStackSize: 120,                 // this is the maximum callstack size of this LStateMinimizeStackMemory: true,          // Defaults to `false` if not specified. If set, the callstack will auto grow and shrink as needed up to a max of `CallStackSize`. If not set, the callstack will be fixed at `CallStackSize`.})
defer L.Close()

Data model

在GopherLua程序中,所有的数据都是LValue。LValue是一个接口类型,具有以下方法:

type LValue interface {String() stringType() LValueType// to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM).assertFloat64() (float64, bool)// to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM).assertString() (string, bool)// to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM).assertFunction() (*LFunction, bool)
}

实现LValue接口的对象是:

Type nameGo typeType() valueConstants
LNilType(constants)LTNilLNil
LBool(constants)LTBoolLTrue, LFalse
LNumberfloat64LTNumber-
LStringstringLTString-
LFunctionstruct pointerLTFunction-
LUserDatastruct pointerLTUserData-
LStatestruct pointerLTThread-
LTablestruct pointerLTTable-
LChannelchan LValueLTChannel-

Go中调用lua

L := lua.NewState()
defer L.Close()
// 加载double.lua
if err := L.DoFile("double.lua"); err != nil {panic(err)
}if err := L.CallByParam(lua.P{// 获取函数名Fn: L.GetGlobal("double"),NRet: 1,Protect: true,}, lua.LNumber(10)); err != nil {panic(err)
}
ret := L.Get(-1) // returned value
L.Pop(1)  // remove received value

Lua支持多个参数和多个返回值,参数好办,用lua.LNumber(123),返回值个数也可以是多个,调用CallByParam的时候,NRet就是返回参数个数,Fn是要调用的全局函数名,Protect为true时,如果没找到函数或者出错不会panic,只会返回err。

**GopherLua的函数调用是通过堆栈来进行的,调用前将参数压栈,完事后将结果放入堆栈中,调用方在堆栈顶部拿结果。**调用完成后,要以压栈的方式,一个一个取回返回值ret := L.Get(-1)。

fib.lua 脚本内容:

function fib(n)if n < 2 then return n endreturn fib(n-1) + fib(n-2)
end

package mainimport ("fmt"lua "github.com/yuin/gopher-lua"
)func main() {// 1、创建 lua 的虚拟机L := lua.NewState()defer L.Close()// 加载fib.lua// Calling DoFile will load a Lua script, compile it to byte code and run the byte code in a LState.// 所以DoFile既加载了文件又执行了字节码if err := L.DoFile(`fib.lua`); err != nil {panic(err)}// 调用fib(n)err := L.CallByParam(lua.P{Fn:      L.GetGlobal("fib"), // 获取fib函数引用NRet:    1,                  // 指定返回值数量Protect: true,               // 如果出现异常,是panic还是返回err}, lua.LNumber(10)) // 传递输入参数nif err != nil {panic(err)}// 获取返回结果ret := L.Get(-1)// 从堆栈中扔掉返回结果// 这里一定要注意,不调用此方法,后续再调用 L.Get(-1) 获取的还是上一次执行的结果// 这里大家可以自己测试下L.Pop(1)// 打印结果res, ok := ret.(lua.LNumber)if ok {fmt.Println(int(res))} else {fmt.Println("unexpected result")}
}

API

Lua调用go

LGFunction类型:type LGFunction func(*LState) int

func main() {L := lua.NewState()defer L.Close()L.SetGlobal("double", L.NewFunction(func(state *lua.LState) int {lv := state.ToInt(1)        /* get argument */L.Push(lua.LNumber(lv * 2)) /* push result */// 返回值个数return 1}))// 加载编译执行hello.luaL.DoFile("./hello.lua")
}

hello.lua中的内容:

print(double(100))

再来一个案例:

package mainimport ("fmt"lua "github.com/yuin/gopher-lua"
)func Add(L *lua.LState) int {// 获取参数arg1 := L.ToInt(1)arg2 := L.ToInt(2)ret := arg1 + arg2// 返回值L.Push(lua.LNumber(ret))// 返回值的个数return 1
}func main() {L := lua.NewState()defer L.Close()// 注册全局函数L.SetGlobal("add", L.NewFunction(Add))// goerr := L.DoFile("main.lua")if err != nil {fmt.Print(err.Error())return}
}

main.lua内容:

print(add(10,20))

打开Lua内置模块的子集

打开Lua内置模块的子集可以通过以下方式实现,例如,可以避免启用具有访问本地文件或系统调用权限的模块。

func main() {L := lua.NewState(lua.Options{SkipOpenLibs: true})defer L.Close()for _, pair := range []struct {n stringf lua.LGFunction}{{lua.LoadLibName, lua.OpenPackage}, // Must be first{lua.BaseLibName, lua.OpenBase},{lua.TabLibName, lua.OpenTable},} {if err := L.CallByParam(lua.P{Fn:      L.NewFunction(pair.f),NRet:    0,Protect: true,}, lua.LString(pair.n)); err != nil {panic(err)}}if err := L.DoFile("main.lua"); err != nil {panic(err)}
}

使用Go创建模块

GopherLua除了可以满足基本的lua需要,还将Go语言特有的高级设计直接移植到lua环境中,使得内嵌的脚本也具备了一些高级的特性

可以使用context.WithTimeout对执行的lua脚本进行超时

可以使用context.WithCancel打断正在执行的lua脚本

多个lua解释器实例之间还可以通过channel共享数据

支持多路复用选择器select

使用Lua作为内嵌脚本的另外一个重要优势在于Lua非常轻量级,占用内存极小。

示例1(官方)

mymodule.go:

package mainimport lua "github.com/yuin/gopher-lua"var exports = map[string]lua.LGFunction{"myfunc": myfunc,
}func myfunc(L *lua.LState) int {return 0
}func Loader(L *lua.LState) int {// register functions to the tablemod := L.SetFuncs(L.NewTable(), exports)// 注册name属性到moduleL.SetField(mod, "name", lua.LString("value"))// returns the moduleL.Push(mod)return 1
}

mymain.go:

package mainimport ("./mymodule""github.com/yuin/gopher-lua"
)func main() {L := lua.NewState()defer L.Close()L.PreloadModule("mymodule", mymodule.Loader)if err := L.DoFile("main.lua"); err != nil {panic(err)}
}

main.lua:

local m = require("mymodule")
m.myfunc()
print(m.name)

示例2

在Go语言的实现中,当我们将一个函数注册为Lua函数(通过L.SetFuncs等方法),它会被转换为LGFunction类型,这样可以确保与Lua C API兼容。返回的整数值用于指示函数的返回值数量(通常用0表示成功,1表示出错)。

实际上,这个整数值在Go的GopherLua实现中没有特别的意义,因为Go语言不需要遵循C API规范。但为了与标准的Lua C API保持一致,Go的GopherLua库仍然要求注册给Lua的函数遵循这个规范,即返回一个整数值。

因此,在Go的GopherLua中,LGFunction类型一定要有返回值,以满足与Lua C API的兼容性需求,即使这个返回值在Go代码中可能并没有特别的实际意义。

package mainimport (lua "github.com/yuin/gopher-lua"
)var exports = map[string]lua.LGFunction{"add": add,
}func add(L *lua.LState) int {// 第一个参数firstArg := L.Get(1).(lua.LNumber)// 第二个参数secondArg := L.Get(2).(lua.LNumber)// 返回值L.Push(firstArg + secondArg)// 返回值个数return 1
}func Loader(L *lua.LState) int {// register functions to the tablemod := L.SetFuncs(L.NewTable(), exports)// returns the moduleL.Push(mod)return 1
}

关闭一个运行的lua虚拟机

L := lua.NewState()
defer L.Close()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// set the context to our LState
L.SetContext(ctx)
err := L.DoString(`local clock = os.clockfunction sleep(n)  -- secondslocal t0 = clock()while clock() - t0 <= n do endendsleep(3)
`)
// err.Error() contains "context deadline exceeded"

虚拟机之间共享lua字节码

调用DoFile会加载Lua脚本,将其编译为字节码并在LState中运行该字节码。

如果您有多个LState,都需要运行相同的脚本,您可以在它们之间共享字节码,这将节省内存。共享字节码是安全的,因为它是只读的,不会被Lua脚本更改。

// CompileLua reads the passed lua file from disk and compiles it.
func CompileLua(filePath string) (*lua.FunctionProto, error) {file, err := os.Open(filePath)defer file.Close()if err != nil {return nil, err}reader := bufio.NewReader(file)chunk, err := parse.Parse(reader, filePath)if err != nil {return nil, err}proto, err := lua.Compile(chunk, filePath)if err != nil {return nil, err}return proto, nil
}// DoCompiledFile takes a FunctionProto, as returned by CompileLua, and runs it in the LState. It is equivalent
// to calling DoFile on the LState with the original source file.
func DoCompiledFile(L *lua.LState, proto *lua.FunctionProto) error {lfunc := L.NewFunctionFromProto(proto)L.Push(lfunc)return L.PCall(0, lua.MultRet, nil)
}// Example shows how to share the compiled byte code from a lua script between multiple VMs.
func Example() {codeToShare := CompileLua("mylua.lua")a := lua.NewState()b := lua.NewState()c := lua.NewState()DoCompiledFile(a, codeToShare)DoCompiledFile(b, codeToShare)DoCompiledFile(c, codeToShare)
}

go-lua调优

预编译

在查看上述 DoString(…) 方法的调用链后,发现每执行一次 DoString(…) 或 DoFile(…) ,都会各执行一次 parse 和 compile 。

func (ls *LState) DoString(source string) error {if fn, err := ls.LoadString(source); err != nil {return err} else {ls.Push(fn)return ls.PCall(0, MultRet, nil)}
}func (ls *LState) LoadString(source string) (*LFunction, error) {return ls.Load(strings.NewReader(source), "<string>")
}func (ls *LState) Load(reader io.Reader, name string) (*LFunction, error) {chunk, err := parse.Parse(reader, name)// ...proto, err := Compile(chunk, name)// ...
}

从这一点考虑,在同份 Lua 代码将被执行多次的场景下,如果我们能够对代码进行提前编译,那么应该能够减少 parse 和 compile 的开销。

package glua_testimport ("bufio""os""strings"lua "github.com/yuin/gopher-lua""github.com/yuin/gopher-lua/parse"
)// 编译 lua 代码字段
func CompileString(source string) (*lua.FunctionProto, error) {reader := strings.NewReader(source)chunk, err := parse.Parse(reader, source)if err != nil {return nil, err}proto, err := lua.Compile(chunk, source)if err != nil {return nil, err}return proto, nil
}// 编译 lua 代码文件
func CompileFile(filePath string) (*lua.FunctionProto, error) {file, err := os.Open(filePath)defer file.Close()if err != nil {return nil, err}reader := bufio.NewReader(file)chunk, err := parse.Parse(reader, filePath)if err != nil {return nil, err}proto, err := lua.Compile(chunk, filePath)if err != nil {return nil, err}return proto, nil
}func BenchmarkRunWithoutPreCompiling(b *testing.B) {l := lua.NewState()for i := 0; i < b.N; i++ {_ = l.DoString(`a = 1 + 1`)}l.Close()
}func BenchmarkRunWithPreCompiling(b *testing.B) {l := lua.NewState()proto, _ := CompileString(`a = 1 + 1`)lfunc := l.NewFunctionFromProto(proto)for i := 0; i < b.N; i++ {l.Push(lfunc)_ = l.PCall(0, lua.MultRet, nil)}l.Close()
}// goos: darwin
// goarch: amd64
// pkg: glua
// BenchmarkRunWithoutPreCompiling-8         100000             19392 ns/op           85626 B/op         67 allocs/op
// BenchmarkRunWithPreCompiling-8           1000000              1162 ns/op            2752 B/op          8 allocs/op
// PASS
// ok      glua    3.328s

虚拟机实例池

新建一个 Lua 虚拟机会涉及到大量的内存分配操作,如果采用每次运行都重新创建和销毁的方式的话,将消耗大量的资源。引入虚拟机实例池,能够复用虚拟机,减少不必要的开销。

type lStatePool struct {m     sync.Mutexsaved []*lua.LState
}func (pl *lStatePool) Get() *lua.LState {pl.m.Lock()defer pl.m.Unlock()n := len(pl.saved)if n == 0 {return pl.New()}x := pl.saved[n-1]pl.saved = pl.saved[0 : n-1]return x
}func (pl *lStatePool) New() *lua.LState {L := lua.NewState()// setting the L up here.// load scripts, set global variables, share channels, etc...return L
}func (pl *lStatePool) Put(L *lua.LState) {pl.m.Lock()defer pl.m.Unlock()pl.saved = append(pl.saved, L)
}func (pl *lStatePool) Shutdown() {for _, L := range pl.saved {L.Close()}
}// Global LState pool
var luaPool = &lStatePool{saved: make([]*lua.LState, 0, 4),
}

README 提供的实例池实现,但注意到该实现在初始状态时,并未创建足够多的虚拟机实例(初始时,实例数为 0),以及存在 slice 的动态扩容问题,这都是值得改进的地方(这是一个可以提交pr的点)。

模块调用

gopher-lua 支持 Lua 调用 Go 模块,在 Golang 程序开发中,我们可能设计出许多常用的模块,这种跨语言调用的机制,使得我们能够对代码、工具进行复用。

package mainimport ("fmt"lua "github.com/yuin/gopher-lua"
)const source = `
local m = require("gomodule")
m.goFunc()
print(m.name)
`func main() {L := lua.NewState()defer L.Close()L.PreloadModule("gomodule", load)if err := L.DoString(source); err != nil {panic(err)}
}func load(L *lua.LState) int {mod := L.SetFuncs(L.NewTable(), exports)L.SetField(mod, "name", lua.LString("gomodule"))L.Push(mod)return 1
}var exports = map[string]lua.LGFunction{"goFunc": goFunc,
}func goFunc(L *lua.LState) int {fmt.Println("golang")return 0
}// golang
// gomodule

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/6206.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【分布式】1、CAP 理论 | 一致性、可用性、分区容忍性

文章目录 一、CAP 理论1.1 Consistency 一致性1.2 Availbility 可用性1.3 Partition Tolerance 分区容忍性1.4 CAP 应用1.4.1 CP1.4.2 AP 二、CAP 实践2.1 ACID2.2 BASE 一、CAP 理论 是 2002 年证明的定理&#xff0c;原文&#xff0c;内容如下&#xff1a; In a distributed…

共享内存在进程间应用

一、共享内存 共享内存是指在内存空间中开辟出一段空间供多个进程使用&#xff0c;它是一种跨进程通信的重要手段。共享内存在多进程开发中应用非常多&#xff0c;特别是在跨进程间大数据量通信时&#xff0c;几乎是必备的选择。工程实践中&#xff0c;安卓的framework中就用到…

喜报|英码科技联合广师大荣获“智芯杯”AI芯片应用创新创业大赛两大奖项

7月15日&#xff0c;由中国仪器仪表学会主办的全国首届“智芯杯”AI芯片应用创新创业大赛总决赛暨颁奖典礼圆满结束&#xff0c;英码科技联合广东技术师范大学设计开发的“AI视觉&#xff0c;让工厂建设更智慧”和“基于AI的智慧校园无感考勤系统”创新项目均荣获三等奖。 ​ 自…

springcloudAlibaba之nacos集群部署和nginx负载均衡

1.环境准备 nacos server安装包&#xff1a;https://github.com/alibaba/nacos nginx安装包&#xff1a;https://nginx.org/en/download.html 2、nacos配置 将下载好的nacos-server的压缩包解压好以后&#xff0c;复制出N份&#xff08;这里取决于你集群的数量&#xff09;&…

设计模式之模板方法模式

例子&#xff1a;登陆&#xff08;普通用户&#xff0c;工作人员&#xff09; 没有使用设计模式实现用户登陆 package com.tao.YanMoDesignPattern.template.notPattern;/*** Author Mi_Tao* Date 2023/7/22* Description* Version 1.0**/ public class LoginModel {private …

Grafana中table的使用技巧

将多个指标数据显示在同一个Table中&#xff0c;需要用到Transform功能&#xff0c;利用Transform功能可以将数据进行处理只显示想要的数据&#xff1a;

【VTK】VTK 让小球动起来,在 Windows 上使用 Visual Studio 配合 Qt 构建 VTK

知识不是单独的&#xff0c;一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏&#xff1a;Visual Studio。 文章目录 版本环境A.uiA.hA.cppRef. 本文主要目的是在 Qt 界面中&#xff0c;显示出来使用 VTK 构建的小球&#xff0c;并让小球能够动起来。同时为了方便对比…

快手营销活动面试

衡量平台业务结果 请求&#xff0c;下发&#xff0c;曝光&#xff0c;点击&#xff0c;点击率&#xff0c;消耗系统QPS&#xff1f; 集群&#xff1a;高峰期8万&#xff0c;平峰期5万单机最大多少&#xff1f;性能瓶颈&#xff1f; 平峰期&#xff1a;300高峰期&#xff1a;500…

【C应用】测试CPU架构是大端还是小端模式

【C应用】测试CPU架构是大端还是小端模式 1、背景2、检测大小端1、背景 大端模式、小端模式是字节序里面的概念,即大端字节序,小端字节序。 关于字节序的理解,请参考文章: 【应用开发】关于字节序的理解 https://jn10010537.blog.csdn.net/article/details/131860480 所谓…

探秘ArrayList源码:Java动态数组的背后实现

探秘ArrayList源码&#xff1a;Java动态数组的背后实现 一、成员变量二、构造器1、默认构造器2、带初始容量参数构造器3、指定collection元素参数构造器 三、add()方法扩容机制四、场景分析1、对于ensureExplicitCapacity&#xff08;&#xff09;方法1.1 add 进第 1 个元素到 …

Inno Setup打包winform、wpf程序可判断VC++和.net环境

Inno Setup打包winform、wpf程序可判断VC和.net环境 1、下载Inno Setup2、新建打包文件、开始打包1、新建打包文件2、填写 应用名称、版本号、公司名称、公司官网3、选择安装路径 Custom是指定默认路径、Program Files folder是默认C盘根目录4、选择程序启动exe文件 以及Addfol…

【Python】基于Python和Qt的海康威视相机开发

文章目录 0 前期教程1 前言2 例程解析3 图像获取4 其他问题与解决办法5 使用到的python包 0 前期教程 【项目实践】海康威视工业相机SDK开发小白版入门教程&#xff08;VS2015OpenCV4.5.1&#xff09; 1 前言 此前写了一篇基于C开发海康威视相机的博客&#xff0c;貌似看的人…

防抖与节流

一、防抖&#xff08;Debounce&#xff09; 一种用于优化性能和减少不必要请求的技术。 防抖函数会延迟触发某个事件处理函数&#xff0c;并在一段时间内只执行一次。如果在延迟时间内多次触发了同一个事件&#xff0c;防抖函数会取消之前的延迟执行&#xff0c;并重新开始计…

springboot实现qq邮箱发送邮件或者验证码

首先我先去qq邮箱或者网易邮箱开通POP3/IMAP/SMTP/Exchange/CardDAV 服务 它在左上角的设置——账户——往下滑就可以找到——然后点击开通 开通后就会得到一串授权码。如下图 接下来直接编写代码 首先我没导入依赖 <!-- 这个是邮箱验证--> <dependency> <group…

Python 模块 ddt 数据驱动测试

简介 ddt 提供了一种方便的方法来实现数据驱动测试&#xff08;Data-Driven Testing&#xff09;。数据驱动测试是一种测试方法&#xff0c;通过将测试数据与测试逻辑分开&#xff0c;可以使用不同的数据集来运行相同的测试用例。这样可以提高测试的灵活性和可维护性&#xff0…

【Deviation】50 Matplotlib Visualizations, Python实现,源码可复现

详情请参考博客: Top 50 matplotlib Visualizations 因编译更新问题&#xff0c;本文将稍作更改&#xff0c;以便能够顺利运行。 本文介绍一下5中图示&#xff1a; Diverging Bars Diverging Texts Diverging Dot Plot Diverging Lollipop Chart with Markers Area Chart 1 Di…

【Spring Cloud】git 仓库新的配置是如何刷新到各个微服务的原理步骤

文章目录 1. 第一次启动时2. 后续直接在 git 修改配置时3. 参考资料 本文描述了在 git 仓库修改了配置之后&#xff0c;新的配置是如何刷新到各个微服务的步骤 前言&#xff1a; 1、假设现有有 3 个微服务&#xff0c;1 个是 配置中心&#xff0c;另外 2 个是普通微服务&#x…

【C++】通过栈和队列学会使用适配器和优先队列学会仿函数的使用

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…

MAL文档

MAL文档 语法读取器宏特殊形式 内置符号内置函数算数运算谓词字符串解释器读取求值打印 原子序列操作字典元数据时间异常FFI 标准库符号函数宏 语法 空白符 所有的空白符会被忽略, 逗号也会被忽略; 以分号起始的内容直到行尾都被视为注释符号 符号中不允许含有空白符及[]{}()&…

pytorch安装GPU版本 (Cuda12.1)教程: Windows、Mac和Linux系统下GPU版PyTorch(CUDA 12.1)快速安装

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…