解释器模式——化繁为简的翻译机
● 解释器模式介绍
解释器模式(Interpreter Pattern)是一种用的比较少的行为型模式,其提供了一种解释语言的语法或表达的方式,该模式定义了一个表达式接口,通过该接口解释一个特定的上下文。在这么多的设计模式中,解释器模式在实际运用上相对来说要少很多,因为我们很少会自己去构造一个语言的文法。虽然如此,既然它能够在设计模式中有一席之地,那么必定有它的可用之处,下面会以最直白的语言来阐述清楚解释器模式是如何工作的。
● 解释器模式的定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
与其他的设计模式不同的是,解释器模式设计编程语言理论知识较多,就拿上面多该模式的定义来说,可能会有很多同学根本看不懂这句话的意思,什么叫文法?为什么加解释器?其又是如何表达的?要彻底搞懂其含义,我们首先要对文法有一个大体的认识。何谓文法?举个简单的例子大家看见就会懂,假设如下短语。
我是程序员
这个只有5个字的短语我相信没人会说看不懂,有同学会问,这与我们要讲的文法有什么关系呢?确实,这一个短语体现不了什么,那么我们再看几条。
我是设计师
我是搬运工
..........
那么上面的这些短语有什么规律呢?上面这些短语中的“我”可以看成主语,而“是”则表示谓语,“程序员”“设计师”和“搬运工”这些名词可以看成宾语,也就是说上面的这些短语都可看成是一个“主谓宾”的结构,而这样的结构我们则称为一条文法,我们可以通过该文法来造成更多符合该文法的语句。当然文法的概念范围非常广,并不局限于主谓宾、定状补这样的语法结构,这是用上面的短语来举例,我们也可以吧上面的这些短语看成是一条“我是【名词】”这样的结构,这也可以看作一条文法。
如果上述文法的概念范围很广,对于我们程序员来说更愿意接受 abcd 这类字符形式的表示方式,假设有如下以 ab 开头 ef 结尾中间排列 N(N>=0) 个 cd 的字符串。
adcd......cdef
随着 N 值的不同具体的字符串也会不一样,我们可以从中得到类似“abef” “abcded” “abcdcdef”之类的字符串。试想一下,是否可以将其表示为一个具体的表达式规则呢?答案是肯定的。在计算机科学中,我们将上述字符串中的“a” “b” “c” “d” “e” 和 “f” 这6个字符称为一种形式语言的字符表,而这些字符组成的集合,如上面的“abcd......cdef”这样有字符表构成的字符串则称之为形式语言,注意,这里是“语言”不是“文法”。假如定义个符号 S (也可以是A、F、G等),从符号 S 发出推导上述字符串,那么就可以得到如下推导式:
S :: = abA*ef
A :: = cd
其中符号“::=”表示推导;符号“*”表示闭包,意思就是符号A可以有0或N个重复;S和A则称非终结符号,因为它们能推导出式子右边的表达式,同时又因为整个推导式是从S出发的,因此,这个S也称为初始符号;而abef和cd这些字符不能再被推导我们将之为终结符号。而上面的推导式意思也很简单,与我们小学时学的因式分解极其相似。像这样的从一个具体的符号出发,通过不断地应用一些产生式规则从而生成一个字符串的集合,我们将描述这个集合的文法称为形式文法,顾名思义,形式文法与形式语言相对应,用来描述形式语言。一般情况下,解释器模式中描述的也是形式语言,定义的也是形式文法,当然你也可以用来描述语言和文法,但它们的范畴都太大,而且对我们编程来说很少会涉猎。
我们对形式语言和形式文法有了一个初步的了解后再来审读一下解释器模式的定义:给定一个语言(如由abcdef六个字符组成的字符串集合),定义它的文法的一种表示(如上面给出的S :: = abA*ef和A :: = cd),定义一个解释器,该解释器使用该表示来解释语言中的句子。这样看起来相对来说是不是更好理解了?现在只有解释器没说清楚了,究竟什么是解释器,它又是如何工作的呢?与其说成解释倒不如说成翻译更好理解,你也可以将解释器简单地理解为一个翻译机。还是用上面的例子中的文法来举例,其就是用来翻译类似“abcd”、“abcdef”和“abcdcdef”之类的字符串句子,这样一来是否对解释器模式的定义有了更进一步的理解了?在编程的时候我们很少会涉及如上那种标准的文法推导式,很多时候会直接使用类似“A*B”这样的字符串规则表达式表示文法,该文法表示“AigeAigeAigeStudio”、“Studio”、“AigeStudio”之类的字符串。
● 解释器模式的使用场景
解释器模式的使用场景其实相当广泛,总的概况下来大致有如下两种。
(1)如果某个简单的语言需要解释执行而且可以将该语言中的语句表示为一个抽象语法树时,可以开始使用解释器模式,
这种场景很好理解,如果一个简单的含有加减运算的数学表达式:p+q+m-n,像这样的表达式其构成无非就两种,一种是以pqmn这类具体参数表示的符号,其无法再被推导,如上面我们所述,其也被称为终结符号;另一种则是以“+”和“-”构成的算术运算符,在该运算符的两边总能找到有意义的具体计算参数,我们也称为非终结符号,如上的数学表达式我们也可以将其表示为一颗抽象语法树,如下图所示。

那么为什么要说是“简单的语言”呢?试想一下,如果我们的表达式是p+q-m/n*x%y^z这样的呢?这个表达式看起来也不算“复杂”,先别急,等我们看完之后的内容你就会知道,这样的一个表达式对于使用解释器模式来解释是对么复杂。
(2)在某些特定的领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则下的语句,然后构建解释器来解释该语句。
如需要将一段阿拉伯数字转换为中午数字,又或者将某个小写英文短句转换为大写,如果aigestudio这样的字符串,我们需要将其转换为大写AIGESTUDIO,这时对于这样的转换来说,其实就是一个不断重复的问题,因为所有的阿拉伯或中文数字和大小写字母都是固定的,也就是说它们都是一个个终结符,不同的只有其具体内容而已,这时间就可以尝试使用解释器模式来解决类似的问题。
● 解释器模式的UML类图
解释器模式的通用类图如下图所示:

根据类图可以得出如下一个解释器模式的通用模式代码
抽象表达式
/*** 抽象表达式*/
public abstract class AbstractExpression {/*** 抽象解释方法** @param ctx 上下文环境对象*/public abstract void interpret(Context ctx);}
终结符表达式
/*** 终结符表达式*/
public class TerminalExpression extends AbstractExpression {@Overridepublic void interpret(Context ctx) {//实现文法中与终结符有关的解释操作}
}
非终结符表达式
/*** 非终结符表达式*/
public class NonterminalExpression extends AbstractExpression {@Overridepublic void interpret(Context ctx) {//实现文法中与非终结符有关的解释操作}
}
上下文黄金类 包含解释器之外的全局信息
/*** 上下文黄金类 包含解释器之外的全局信息*/
public class Context {
}
客户类
/*** 客户类*/
public class Client {public static void main(String[] args) {//根据文法对特定句子构建抽象语法树后解释}
}
角色介绍。
AbstractExpression:抽象表达式。
声明一个抽象的解释操作父类,并定义一个抽象的解释方法,其具体的实现在各个具体的子类解释器中完成。
TerminalExpression:终结符表达式。
实现文法中与终结符有关的解释操作。文法中每一个终结符都有一个具体的终结表达式与之对应。
NonterminalExpression:非终结符表达式。
实现文法中与非终结符有关的解释操作。
Context:上下文环境类。
包含接收器之外的全局信息。
Client:客户类。
解析表达式,构建抽象语法树,执行具体的解释操作等。
● 解释器模式的简单实现
开头所说,解释器模式的应用范围相当广泛,一个比较常见的场景是对算术表达式的解释,如表达式“m+n+p”,如果我们使用解释器模式对该表达式进行解释,那么代表数字的m、n、和p三个字母我们就可以看成是终结符号,而“+”这个运算符号则可以当作非终结符号。同样地,我们可以先创建一个抽象解释器表示数学运算。
抽象的算术运算解释器 为所有解释器共性提前
/*** 抽象的算术运算解释器 为所有解释器共性提前*/
public abstract class ArithmeticExpression {/*** 抽象的解析方法* 具体的解析逻辑由具体的子类实现** @return 解析到的具体值*/public abstract int interpret();
}
在该抽象解析器的解释方法interpret中,我们没有像前面的例子那样使用一个Context对象作为interpret方法的签名,在本例中运算的结果都是作为参数返回,因此,没有必要使用额外的对象储存信息。ArithmeticExpression 有两个直接子类 NumExpression 和 OperatorExpression ,其中NumExpression 用于对数字进行解释。
数字解释器 仅仅为了解释数字
/*** 数字解释器 仅仅为了解释数字*/
public class NumExpression extends ArithmeticExpression {private int num;public NumExpression(int num) {this.num = num;}@Overridepublic int interpret() {return num;}
}
代码很简单,逻辑也很明确,就不多说了。OperatorExpression 依然是一个抽象类,其声明两个 ArithmeticExpression 类型的成员变量储存运算符两边的数字解释器,这两个成员变量会在构造方法中被赋值。
运算符号抽象解释器 为所有运算符号解释器共性的提取
/*** 运算符号抽象解释器 为所有运算符号解释器共性的提取*/
public abstract class OperatorExpression extends ArithmeticExpression {//声明两个成员变量存储运算符号两边的数字解析器private ArithmeticExpression exp1, exp2;public OperatorExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) {this.exp1 = exp1;this.exp2 = exp2;}
}
OperatorExpression 也有一个直接子类,AdditionExpression ,顾名思义其表示对加法运算进行解释,其逻辑都很简单,就不做过多说明了。
加法运算抽象解释器
/*** 加法运算抽象解释器*/
public class AdditionExpression extends OperatorExpression {public AdditionExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) {super(exp1, exp2);}@Overridepublic int interpret() {return exp1.interpret() + exp2.interpret();}
}
上面就是本例中所要使用到的所有解释器。除此之外,我们创建有跟一个 Calculator 类来处理一些相关的业务。
处理与解释相关的一些业务
/*** 处理与解释相关的一些业务*/
public class Calculator {//声明一个Stack栈存储并操作所有相关解释器private Stack<ArithmeticExpression> mExpStack = new Stack<>();public Calculator(String expression) {//声明两个ArithmeticExpression类型的临时变量,存储运算符左右两边的数字解释器ArithmeticExpression exp1, exp2;//根据空格分隔表达式字符串String[] elements = expression.split(" ");//循环遍历表达式元素数组for (int i = 0; i > elements.length; i++) {//判断运算符号switch (elements[i].charAt((0))) {case '+'://如果是加号//则将栈中的解释器弹出作为运算符号左边的解释器exp1 = mExpStack.pop();//同时将运算符号数组下标下一个元素构造为一个数字解释器exp2 = new NumExpression(Integer.valueOf(elements[++i]));//通过上面两个数字解释器构造加法运算加法运算解释器mExpStack.push(new AdditionExpression(exp1, exp2));break;default://如果有数字//如果不是运算符则为数字//若是数字,直接将构造函数解释器并压入栈mExpStack.push(new NumExpression(Integer.valueOf(elements[i])));break;}}}/*** 计算结果** @return 最终的计算结果*/public int calculate() {return mExpStack.pop().interpret();}
}
这里要注意的是,为了简化问题我们约定表达式字符串的每个元素直接必须使用空格间隔开,如“1 + 22 + 333 + 4444”这样的表达式字符串则是合法的,而“1+22+333+4444”则不合法,因此,我们才能在Calculator的构造方法中通过空格来拆分字符串。Calculator 类的逻辑很好理解,这里还是以“1 + 22 + 333 + 4444”这个字符串为例,首先将其拆分为有7个元素组成的字符串数组,然后循环遍历,首先遍历到的元素为1,那么将其作为参数构造一个 NumExpression 对象压入栈,其次是加号运算符,此时我们则将刚压入栈的由元素1作为参数构造的 NumExpression 对象抛出作为加号运算符左边的数字接收器,而右边的解释器呢?我们只需要将当前数组下标+1获取到的数组元素中加号右边的数字22,将其作为参数构造一个 AdditionExpression 加法解释器对象压入栈中即可,这个过程其实就是在构建语法树,只不过我们将其单独封装在了一个类里而不是哦在 Client 客户类里进行,最后,我们公布一个 calculate 方法执行解释并返回结果。后面的 Client 客户类就很简单了,构造一个 Calculator 对象,调用 calculate 方法输出结果即可。
客户类
/*** 客户类*/
public class Client {public static void main(String[] args) {Calculator c = new Calculator("153 + 3589 + 118 + 555");System.out.println(c.calculate());}
}
上面我们只是简单地对加法运算定义了解释器,如果现在又想引入减法运算怎么办呢?很简单,定义一个减法解释器即可。
减法运算抽象解释器
/*** 减法运算抽象解释器*/
public class SubtractionExpression extends OperatorExpression {public SubtractionExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) {super(exp1, exp2);}@Overridepublic int interpret() {return exp1.interpret() - exp2.interpret();}
}
同样地,SubtractionExpression 也集成于 OperatorExpression ,表示对运算符号的解释器,其实现逻辑也很简单不再多说。然后我们还需修改一下 Calculator 类中构建语法树的逻辑,添加一条对“-”号的分支判断处理即可。
case '-'://如果是减号exp1 = mExpStack.pop();exp2 = new NumExpression(Integer.valueOf(elements[i]));mExpStack.push(new SubtractionExpression(exp1, exp2));break;
这样,我们这可以处理我们的减法运算了。
/*** 客户类*/
public class Client {public static void main(String[] args) {Calculator c = new Calculator("153 + 3589 + 118 - 555 - 597 - 66");System.out.println(c.calculate());}
}
具体的输出结果就不多说了,大家可以自行尝试,这里我们可以看到接收器模块的一个优点,就是灵活性强,上面的例子中我们只实现了对加减法的解释计算,如果想实现更多的运算法则,如乘除取余等,只需要创建对应的运算接收器即可,但是混合运算要比简单的加减法运算复杂得多,还要考虑不同的符号的运算优先级,这也是文章开头我们说,在“简单的语言”中适用解释器模式。
从上面的两个例子中可以看到,具体的文法规则与解释器之间其实是有对应关系的,大多数情况下两者之间是一一对应的关系,即一条文法对应一个解释器,当然,我们也可以为一条文法创建多个不同的解释器,但是反过来就不行。这个很好理解,如上例中对于加法解释器我们实现的是对加法运算的解释,其对应一个解释器 AdditionExpression ,当然也可以在为其创建一个解释器XXXXXXExpression ,但是一个解释器却不能即解释加法运算又解释减法运算,否则就违背了解释器模式的定义。说的解释器模式的定义,我们提到过抽象语法树,而我们在上面两个例子中都有构造抽象语法树的相关逻辑,第一个例子中我们在客户类里构建由17个数字和一个数字或字母构成的语法树;而第二个例子则根据具体表达式动态创建相应的语法树。从这点可以看出解释器模式并不包含对抽象语法树的构建,其构建逻辑应由客户根据具体的情况其生成。
将一条具体的文法通过一个解释器解释,把复杂的文法规则分离为简单的功能进行解释,最后将其组合成一颗抽象语法树解释执行。至此,我们可以看到解释器模式的原理与本质:将复杂的问题简单化、模块化,分离实现、解释执行。
相关文章:
解释器模式——化繁为简的翻译机
● 解释器模式介绍 解释器模式(Interpreter Pattern)是一种用的比较少的行为型模式,其提供了一种解释语言的语法或表达的方式,该模式定义了一个表达式接口,通过该接口解释一个特定的上下文。在这么多的设计模式中&…...
【凡人修仙传】定档,四女神出场,韩立遭极阴岛陷阱,蛮胡子亮相
【侵权联系删除】【文/郑尔巴金】 距离凡人修仙传动画星海飞驰序章完结,已经过去了两个月的时间,相信大家等待的心情相当难熬,而且也愈发期待韩立结丹后在乱星海发生的故事。按照官方当初立下的FLAG,新年番动画即将在金秋十一月上…...
【解决】设置pip安装依赖包路径默认路径在conda路径下,而不是C盘路径下
【解决】设置pip安装依赖包路径默认路径在conda路径下,而不是C盘路径下 问题描述 在win11下安装miniconda,在conda环境里使用pip安装,依赖包总是安装到C盘路径,如 C:\Users\Jimmy\AppData\Local\Programs\Python\Python311\Lib\…...
JoySSL-新兴国产品牌数字证书
随着我国对数据安全重视程度的不断提升,国产SSL证书越来越受到广大政府机关和企业的青睐,成为提升网站数据安全能力的重要技术手段。那么什么是国产SSL证书?国产SSL证书和普通SSL证书又有什么区别呢? 什么是国产SSL证书ÿ…...
kafka3.X基本概念和使用
kafka基本概念和使用 文章目录 kafka基本概念和使用 kafka的概念基本概念Kafka的使用 首先kafka的安装kafka的简单实用和理解搭建集群(3个节点)windows版本环境搭建 本文"kafka的概念"部分是在[初谈Kafka][ https://juejin.im/post/5a8e7f…...
用低代码平台代替Excel搭建进销存管理系统
目录 一、用低代码平台搭建系统 1.需求调研 2.基于痛点梳理业务流程 3.低代码实现 (1)基础资料模块 (2)采购管理模块 (3)销售管理模块 (4)库存管理模块 (5&…...
Redis和Memcached网络模型详解
1. Redis单线程单Reactor网络模型 1.1 redis单线程里不能执行十分耗时的流程,不然会客户端响应不及时 解决方法一: beforesleep里删除过期键操作若存在大量过期键时,会耗费大量时间,redis采用的策略之一就是采用timelimit方案超过…...
二叉搜索树的实现(递归方式)
目录 实现思路 插入操作 删除操作 完整代码 测试案例 总结 二叉搜索树(Binary Search Tree,BST)是一种常用的数据结构,它具有以下特点: 左子树上所有节点的值均小于它的根节点的值右子树上所有节点的值均大于它的…...
NetCore IIS Redis JMeter 登录压力测试
近期,由于某项目验收需要,需要登录接口同时满足至少400个账号同时并发登录,于是开始编写测试代码,以满足项目业务需要。首先,安装jdk,由于本机已安装jdk8: 如果你机器上没有安装jdk,…...
进一步了解视频美颜SDK:美颜SDK的技术原理
美颜技术在当今的数字世界中变得越来越流行,尤其是在视频直播、社交媒体和视频通话应用中。用户寻求通过美颜效果增强自己的外观,这种需求催生了众多美颜SDK(软件开发工具包)的出现。这些SDK使开发者能够轻松地将美颜功能集成到他…...
【Qt之QSetting】介绍及使用
概述 QSettings类提供了一种持久的、与平台无关的应用程序设置存储功能。 用户通常期望一个应用能在不同会话中记住其设置(窗口大小和位置,选项等)。在Windows上,这些信息通常存储在系统注册表中;在macOS和iOS上&…...
基于WebRTC构建的程序因虚拟内存不足导致闪退问题的排查以及解决办法的探究
目录 1、WebRTC简介 2、问题现象描述 3、将Windbg附加到目标进程上分析 3.1、Windbg没有附加到主程序进程上,没有感知到异常或中断 3.2、Windbg感知到了中断,中断在DebugBreak函数调用上 3.3、32位进程用户态虚拟地址和内核态虚拟地址的划分 …...
通过jdk自制https证书并配置到nginx并配置http2
生成证书 这里使用自己生成的免费证书。在${JAVA_HOME}/bin 下可以看到keytool.exe,在改目录打开cmd然后输入: keytool -genkey -v -alias lgq.com -keyalg RSA -keystore d:/zj/ssl/fastfly.com.keystore -validity 3650生成证书过程中:【你的名字】对…...
祝贺中国煤科重庆研究院和达索、百世慧PLM项目顺利结项
引言 2023年10月17日,中国煤科重庆研究院与达索系统、百世慧在重庆研究院会议室召开了产品全生命周期管理(PLM)系统结项会。中国煤科重庆研究院科技发展部副主任孙海涛、测控技术研究分院副院长于庆、重庆大学教授鄢萍、达索公司工业装备部南…...
基于springboot实现数码论坛系统设计与实现系统【项目源码+论文说明】
基于springboot实现数码论坛系统设计与实现系统演示 摘要 网络的广泛应用给生活带来了十分的便利。所以把数码论坛与现在网络相结合,利用java技术建设数码论坛系统,实现数码论坛的信息化。则对于进一步提高数码论坛发展,丰富数码论坛经验能起…...
魔域开服需要什么样的配置
魔域是一个非常受玩家喜欢的游戏,是一款大型魔幻题材的网络游戏,关于魔族入侵亚特大陆的故事,玩家在游戏里扮演不同的角色捍卫大陆安全,很多玩家想要更多的体验就会选择开新服,今天就让小编来讲一讲魔域开服要什么配置…...
7个好用的PC端设计软件,设计必看!优漫动游
身为设计师的你是不是还在为到处寻找合适的设计软件而烦恼?是不是在为因为没能用上好的软件而影响自己设计生涯的事情感到焦虑?别担心,你的烦恼可能每个成熟的设计师都遇到过,但他们最终都走了过来。借助以下7个好用的PC端设计软件…...
10-动画animation
动画animation 动画-过渡和动画之间的异同-animation-name 指定要绑定到选择器的关键帧的名称,告诉系统需要执行哪个动画-animation-duration 动画指定需要多少秒或毫秒完成,告诉系统动画持续的时长-animation-timing-function 设置动画将如何完成一个周…...
【带头学C++】----- 1.基础知识 ---- 1.24 逻辑控制语句
1.24 逻辑控制语句 本节主要学习关于C逻辑控制的一些语句的用法,结合实践代码总结一下。 1.24.1 if以及if - else(条件语句) 1.if语句: if(条件){执行语句; }//一旦执行if语句,先判断()里的条件是否满足,…...
微信公众号分销商城源码系统+多元商家+收银台 带完整的搭建教程
给大家推荐一款微信公众号分销商城源码系统,这是一个全新三级分销商城,功能十分丰富。一起来看看你吧。 微信公众号分销商城的功能: 1.商品展示和推广:商家可以在商城中展示商品信息,包括商品名称、价格、库存等&#…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
