HNU编译原理实验一cminus_compiler-2022-fall

news2025/1/8 6:05:23

前言:实验不是很难,主要考察正则表达式部分

lab1实验报告

实验要求

根据cminux-f的词法补全lexical_analyer.l文件,完成词法分析器,能够输出识别出的tokentype ,line(刚出现的行数),pos_start(该行开始位置),pos_end(结束的位置,不包含)

文本输入:

int a;

则识别结果应为:

int     280     1       2       5
a       285     1       6       7
;       270     1       7       8

对于部分token,我们只需要进行过滤,即只需被识别,但是不应该被输出到分析结果中。因为这些token对程序运行不起到任何作用。

实验难点

  1. 实验环境的搭建以及配置,比如ubuntu18.04的安装以及flex编译环境的配置等。
  2. 学习并掌握cminus-f语法,提供其词法的正则表达式。
  3. 学会利用FLEX并使用正则表达式来编写正确的词法分析器程序,能够识别各种词语

实验设计

1.实验要求能够识别出所有的输入token,但并不是所有的内容都需要识别。具体需要识别的token在 lexical_analyzer.h 定义了,打开lexical_analyzer.h 并查看具体要识别的token以及其对应的字符和含义,符号的编号和具体内容如下:

//运算
ADD = 259, 	/* 加号:+ */
SUB = 260, 	/* 减号:- */
MUL = 261, 	/* 乘号:* */
DIV = 262, 	/* 除法:/ */
LT = 263, 	/* 小于:< */
LTE = 264, 	/* 小于等于:<= */
GT = 265, 	/* 大于:> */
GTE = 266, 	/* 大于等于:>= */
EQ = 267, 	/* 相等:== */
NEQ = 268, 	/* 不相等:!= */
ASSIN = 269,/* 单个等于号:= */
    
//符号
SEMICOLON = 270,	/* 分号:; */
COMMA = 271, 		/* 逗号:, */
LPARENTHESE = 272, 	/* 左括号:( */
RPARENTHESE = 273, 	/* 右括号:) */
LBRACKET = 274, 	/* 左中括号:[ */
RBRACKET = 275, 	/* 右中括号:] */
LBRACE = 276, 		/* 左大括号:{ */
RBRACE = 277, 		/* 右大括号:} */
    
//关键字
ELSE = 278, 	/* else */
IF = 279, 		/* if */
INT = 280, 		/* int */
FLOAT = 281, 	/* float */
RETURN = 282, 	/* return */
VOID = 283, 	/* void */
WHILE = 284,	/* while */
    
//ID和NUM
IDENTIFIER = 285,	/* 变量名,例如a,b */
INTEGER = 286, 		/* 整数,例如1,2 */
FLOATPOINT = 287,	/* 浮点数,例如1.1,1.2 */
ARRAY = 288,		/* 数组,例如[] */
LETTER = 289,		/* 单个字母,例如a,z */	
    
//others
EOL = 290,			/* 换行符,\n或\0 */	
COMMENT = 291,		/* 注释 */
BLANK = 292,		/* 空格 */
ERROR = 258			/* 错误 */

2.知道了要识别的token的具体含义,就可以写出其对应的正则表达式。参考 C-Minus文法 来写出各个要识别的token的正则表达式。

在这里插入图片描述

因此,token的type和对应的正则表达式如下,其中:

  • 对于运算符,符号等token的正则表达式即为其对应的英文状态下的半角符号。

  • 对于关键字等token,在c-minus-f中的语法中的正则表达式为其对应的小写状态。

  • 接着ID和NUM的正则表达式比较复杂,使用语法糖可以有效来简化构造。

    • 对于变量名,可以是a-z或A到Z之间的一个或多个字母的组合,从a-z和A-Z中选一个的正则表达式为[a-zA-Z],选一个或多个的话加一个+号即可,即[a-zA-Z]+。

    • 对于整数,就是从0-9里选一个或多个数,则正则表达式为[0-9]+,也可以使用[\d]+(不知道c-minus-f支不支持)。

    • 对于浮点数,有一个问题就是算不算正负数,这里我觉得应该是不算的,如果是负的浮点数前面的符号应该当做‘-’来匹配。要注意的一点就是.号前要加\转义符,否则会将.当做任意字符来匹配。小数点前应该有1或多个数组,不然比如.123这样也不叫浮点数,小数点后同理。

    • 数组这里也有一个坑,就是[和]和[]是三个东西(题目也给出了),如果要匹配数组[],则两个框都要加转义符从而可以匹配。

  • 其他符号

    • 换行符的正则表达式就是\n,由于可以实现一到多行的换行,所以要在后面加个+(实现一个或多个换行)。

    • 注释符的正则表达式是最复杂的,题目要求注释直接不能有嵌套,可能比如类似/* */ */这样。首先注释的开头和结尾为/**/,由于都是特殊字符,所以都需要加上转义符用于匹配。在中间部分,想法就是如果开始注释符 /*之后的注释内容中要么就不包含*,要么就如果包含了一个或多个 * 的话,其后面的注释内容不能跟着*/(因为最末尾还有着*/作为注释的结束符作为匹配)。所以中间注释内容部分,要么不含 * ,可以用[^\*]表示,\是转义符,代表着可以匹配除 * 以外的所有内容。如果包含 * ,可以是一个或多个,那么可以用 * 的闭包表示,然后其后接的符号不能为*/,所以最后的注释的正则表达式为\/\*([^\*]|(\*)*[^\*\/])*(\*)*\*\/ https://blog.csdn.net/qq_45795586/article/details/122309981

      图片来源

    • 空白符的正则表达式可以有很多种,比如正则表达式手则中可以这么表示。实际上也可以直接打一个空格 来表示空格的正则表达式,(为了实现一个或多个空格,还需要在后面加上+,即[ ]+。注意:[ ]+这个正则表达式有错误,我也不知道哪里错了,可能默认空格只有一个)还是使用[ \f\n\r\t\v]作为空白符的正则表达式,除此以为" "也能当做空白符的正则表达式
      在这里插入图片描述

    • 对于error错误,我认识error错误可以匹配任何字符,所以用.来表示错误,代表 匹配除“\n”之外的任何单个字符。

对于写出的正则表达式,可以在 正则表达式在线测试 | 菜鸟工具 (runoob.com) 进行测试。

tokentype正则表达式
ADD259+
SUB260-
MUL261*
DIV262/
LT263<
LTE264<=
GT265>
GTE266>=
EQ267==
NEQ268!=
ASSIN269=
SEMICOLON270;
COMMA271,
LPARENTHESE272(
RPARENTHESE273)
LBRACKET274[
RBRACKET275]
LBRACE276{
RBRACE277}
ELSE278else
IF279if
INT280int
FLOAT281float
RETURN282return
VOID283void
WHILE284while
IDENTIFIER285[a-zA-Z]+
INTEGER286[0-9]+
FLOATPOINT287[0-9]+.[0-9]+
ARRAY288\[\]
LETTER289[a-zA-Z]
EOL290[\n]+
COMMENT291`/*([^*]
BLANK292[ \f\n\r\t\v]
ERROR258.

3.写出指定模式匹配时对应的动作

实验文档的基础知识中给出了如何写一个简单的单词数量统计的程序

%{
//在%{和%}中的代码会被原样照抄到生成的lex.yy.c文件的开头,您可以在这里书写声明与定义
#include <string.h>
int chars = 0;
int words = 0;
%}

%%
 /*你可以在这里使用你熟悉的正则表达式来编写模式*/
 /*你可以用C代码来指定模式匹配时对应的动作*/
 /*yytext指针指向本次匹配的输入文本*/
 /*左部分([a-zA-Z]+)为要匹配的正则表达式,
 	右部分({ chars += strlen(yytext);words++;})为匹配到该正则表达式后执行的动作*/
[a-zA-Z]+ { chars += strlen(yytext);words++;}


. {}
 /*对其他所有字符,不做处理,继续执行*/

%%

int main(int argc, char **argv){
    //yylex()是flex提供的词法分析例程,默认读取stdin      
    yylex();                                                               
    printf("look, I find %d words of %d chars\n", words, chars);
    return 0;
}

可以看到,在第二部分要求我们使用熟悉的正则表达式来编写模式,这也是题目要求补全lexical_analyer.l文件中的一部分内容,那么可以仿照上面的程序来编写出对应的动作

yytext指针指向本次匹配的输入文本,我们在左部分写要匹配的正则表达式,右部分为匹配到该正则表达式后执行的动作。 除了一些特殊的字符要在analyzer函数中实现外,其余部分的实现都是差不多的。题目要求能够输出识别出的token,type ,line(刚出现的行数),pos_start(该行开始位置),pos_end(结束的位置,不包含) 。首先将开始位置和结束位置设置成一样,代表当前识别的字符从上一识别完的字符的末尾开始。接着设置pos_end+=strlen(yytext) ,strlen(yytext)为这次识别到的长度,然后返回识别出的token即可。

代码如下:

 /****请在此补全所有flex的模式与动作  start******/
 //STUDENT TO DO
 /******** 运算 ********/
\+ {pos_start=pos_end;pos_end+=strlen(yytext);return ADD;}
\- {pos_start=pos_end;pos_end+=strlen(yytext);return SUB;}
\* {pos_start=pos_end;pos_end+=strlen(yytext);return MUL;}
\/ {pos_start=pos_end;pos_end+=strlen(yytext);return DIV;}
\< {pos_start=pos_end;pos_end+=strlen(yytext);return LT;}
\<\= {pos_start=pos_end;pos_end+=strlen(yytext);return LTE;}
\> {pos_start=pos_end;pos_end+=strlen(yytext);return GT;}
\>\= {pos_start=pos_end;pos_end+=strlen(yytext);return GTE;}
\=\= {pos_start=pos_end;pos_end+=strlen(yytext);return EQ;}
\!\= {pos_start=pos_end;pos_end+=strlen(yytext);return NEQ;}
\= {pos_start=pos_end;pos_end+=strlen(yytext);return ASSIN;}

/******** 符号 ********/
\; {pos_start=pos_end;pos_end+=strlen(yytext);return SEMICOLON;}
\, {pos_start=pos_end;pos_end+=strlen(yytext);return COMMA;}
\( {pos_start=pos_end;pos_end+=strlen(yytext);return LPARENTHESE;}
\) {pos_start=pos_end;pos_end+=strlen(yytext);return RPARENTHESE;}
\[ {pos_start=pos_end;pos_end+=strlen(yytext);return LBRACKET;}
\] {pos_start=pos_end;pos_end+=strlen(yytext);return RBRACKET;}
\{ {pos_start=pos_end;pos_end+=strlen(yytext);return LBRACE;}
\} {pos_start=pos_end;pos_end+=strlen(yytext);return RBRACE;}

/******** 关键字 ********/
else {pos_start=pos_end;pos_end+=strlen(yytext);return ELSE;}
if {pos_start=pos_end;pos_end+=strlen(yytext);return IF;}
int {pos_start=pos_end;pos_end+=strlen(yytext);return INT;}
float {pos_start=pos_end;pos_end+=strlen(yytext);return FLOAT;}
return {pos_start=pos_end;pos_end+=strlen(yytext);return RETURN;}
void {pos_start=pos_end;pos_end+=strlen(yytext);return VOID;}
while {pos_start=pos_end;pos_end+=strlen(yytext);return WHILE;}

/******** ID和NUM ********/
[a-zA-Z]+ {pos_start=pos_end;pos_end+=pos_start+strlen(yytext);return IDENTIFIER;}
[0-9]+ {pos_start=pos_end;pos_end+=pos_start+strlen(yytext);return INTEGER;}
[0-9]+\.[0-9]+ {pos_start=pos_end;pos_end+=pos_start+strlen(yytext);return FLOATPOINT;}
\[\] {pos_start=pos_end;pos_end+=strlen(yytext);return ARRAY;}
[a-zA-Z] {pos_start=pos_end;pos_end+=strlen(yytext);return LETTER;}

/******** others ********/
[\n]+ {pos_start = 1;pos_end = 1;lines+=strlen(yytext);return EOL;}
\/\*([^\*]|(\*)*[^\*\/])*(\*)*\*\/ {return COMMENT;}
[ \f\n\r\t\v] {pos_start = pos_end;pos_end+=strlen(yytext);return BLANK;}
. {return ERROR;}
 /****请在此补全所有flex的模式与动作  end******/

4.补充 analyzer函数

实验要求补充完整lexical_analyer.l文件,其中flex的模式与动作只是一部分,另一部分就是在analyzer函数中补充关于匹配到注释COMMENT,空格BLANK以及换行EOL后执行的动作。由于在上一步已经写了关于空格BLANK以及换行EOL后执行的动作,所以这里只用写注释COMMENT的动作即可,其他留空就行。因为在步骤2时已经return ,所以这里只需要写关于pos_start,pos_end以及lines的相关变化

  • 对于注释COMMENT,先获yytext的长度为len,然后通过while循环判断注释中是否存在换行符,即判断yytext[i]是否为换行符,如果是换行\n的话,将pos_start和pos_end设置为1,lines加1;否则pos_end++;当循环结束则break;
case COMMENT:
	//STUDENT TO DO
	for(int i = 0;i < strlen(yytext);i++)
	{
		pos_end++;
         if( yytext[i] == '\n' )
         {
			lines += 1;pos_end = 1;
         }
     }
     break;

实验结果验证

1.创建build文件夹,配置编译环境, 执行指令make lexer运行代码,开始编译,如图所示,编译成功

在这里插入图片描述

2.执行指令python3 ./tests/lab1/test_lexer.py来查看是否通过6个测试样例 。可以看到,通过了6个测试样例

在这里插入图片描述

3.验证结果正确性。输入diff ./tests/lab1/token ./tests/lab1/TA_token 将自己的生成结果和助教提供的TA_token进行比较。如果结果完全正确,则没有任何输出结果。如果有不一致,则会汇报具体哪个文件哪部分不一致。如图所示,是正确的。

在这里插入图片描述

4.自行设计testcase进行测试。

代码如下

+-*/<<=>>====!=;()[]{}
if else float return
int a[10];
int a[];
void while
1.  0.123
/*/*test*/*/

运行结果如下./build/lexer ./tests/lab1/testcase/test.cminus out

这个测试样例几乎把所有的token都测试了一遍,主要测试的地方如下:

  1. 对于第一行中的<=>>====部分,按照从左往右识别的顺序应该是<=,>,>=,==,=。通过测试结果可以看到可以正确识别出来
  2. 第二个要测试的地方是关于数组。 [, ], 和 [] 是三种不同的token。[]用于声明数组类型,[]中间不得有空格。 可以看到也能正确识别出来。
  3. 第三个测试的地方是注释的嵌套。对于/*/*test*/*/这个语句,如果支持嵌套注释的话那么/*test*/是外层注释的注释内容,识别结果应该不会任何内容。但是测试结果显示最后识别出来了*/,说明不支持嵌套注释。
  4. 对于其他的部分测试结果也能正确查询出来,一些关键字也不会识别成变量名等。有一个问题就是关于浮点型,对于1.0.这样的数也算作浮点型,这是有点疑问的地方。

实验反馈

​ 这次实验的收获还是挺大的。首先对于正则表达式的各种写法有了更深的理解。比如对于数字的正则表达式,就有[0-9]+\d等几种不同的写法。对于一些比较复杂但有有规律的正则表达式,使用语法糖可以有效的简化结构,比如英文字符,正则表达式为a|b|c|d……|z,使用语法糖可以[a-z]直接表示,十分简便。我还加深了对cminus-f词法的理解。另外我还学会了如何简单使用FLEX,学会了如何利用FLEX生成一个生成词法分析器,补充完了一个识别token的程序,从而实现对输入的词法进行识别和分析的功能,并能准确给出token所在行,开始位置和结束位置。

​ 同时在做这次实验也遇到了一些困难,不过不是关于实验本身的。第一个问题就是环境的配置,虽然之前使用的Ubuntu18.04,实验推荐的是Ubuntu20.04,并且实验要求下载很多配件,比如flex,LLVM , Bison等。下载过程老是出错,换了很多源找了很多教程才弄好。第二个比较头疼的问题就是gitee的使用问题,由于之前没使用过gitee,对于gitee上的很多操作都很不熟悉,特别是将项目同步上传,试了很久都没有成功。

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

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

相关文章

[机缘参悟-95] :不同人生、社会问题的本质

事情的本质是物极必反&#xff08;轮回、周期&#xff09; 社会的本质是优胜劣汰&#xff08;迭代、发展&#xff09; 道德的本质是伦理秩序&#xff08;未定、秩序&#xff09; 战争的本质是资源占用&#xff08;弱肉、强食&#xff09; 商业的本质是价值交换 金钱的本质…

基于JAVA SpringBoot+ JWT+Redis的ERP系统,VUE+Element-UI 前后端分离的Saas平台

项目简介 简云Saas平台 基于SpringBoot2.2.0, Mybatis, JWT, Redis, VUEElement-UI 的前后端分离的Saas平台管理系统 在线报表开发 在线表单设计 工作流设计 自定义打印模板定义 **产品分二个版本: 开源版本(包含了系统基础架构在线表单设计). 此版本代码完全开源ERP版本…

Flink-基本的合流操作

文章目录1.基本的合流操作2.1联合&#xff08;Union&#xff09;2.2 连接&#xff08;Connect&#xff09;2.基于时间的合流——双流联结&#xff08;Join&#xff09;2.1 窗口联结&#xff08;Window Join&#xff09;2.2 间隔联结&#xff08;Interval Join&#xff09;2.3 窗…

《面向对象分析与设计》总结

面向对象的软件工程1 面向对象的演化1.1 生活中复杂系统的特点1.2 软件系统的复杂性1.2.1 复杂性的四个方面1.2.1.1 问题域的复杂性1.2.1.2 管理开发的困难性1.2.1.3 软件中的灵活性1.2.1.4 描述离散系统行为1.2.2 复杂系统的五个属性1.2.2.1 层次结构1.2.2.1.1 对象结构1.2.2.…

数据分析神器:数据自动录入并生成BI报表

做报表、分析数据、做汇报是许多打工人的日常&#xff0c;每天都要耗费不少的时间用Excel来整理、清洗数据和生成好看的报表。如果这些数据都是手动整理、复制粘贴的话&#xff0c;不仅费时费力&#xff0c;而且很容易出错。 在越来越多企业采用SaaS产品和不同数据应用的今天&…

来看一个vue-element-表单之登录页面,最后送上一个登录页面

vue-element 表单之登录页面使用 0. 先留下属性表格 表单验证&#xff1a;Form 组件提供了表单验证的功能&#xff0c;只需要通过 rules 属性传入约定的验证规则&#xff0c;并将 Form-Item 的 prop 属性设置为需校验的字段名即可。 1. 表单属性表(el-form) 2. 表单项属性表…

php工作流引擎再发新版本—Tpflow7.0重磅发布

2022年已接近尾声&#xff0c;又到了每年发布大版本的时候&#xff0c;Tpflow历经一个多月的意见征集及版本优化&#xff0c;从底层改进&#xff0c;从UI调整&#xff0c;增强了事件功能。 发布日期&#xff1a;2022年12月23日 发布版号&#xff1a;V7.0.0 Tpflow 工作流引擎…

短视频引流+私域流量沉淀,一个全新的短视频和链动模式结合方案

在微盟企微助手微盟智慧零售团队的协助下&#xff0c;今年7月底么么茶正式开始运营企微私域&#xff0c;截至当前&#xff0c;在短短3个月时间已成功沉淀7万私域客户&#xff0c;线上商城GMV超145万。 么么茶旅拍的核心流量来源自公域短视频平台&#xff0c;品牌基于服务覆盖下…

OB0206 obsidian 表格编辑插件:advanced Tables插件使用

序号解读&#xff1a; 01——软件基础使用、基础语法 02——插件使用 03——综合实战 0 写在前面 Ob社区插件汇总&#xff1a;Airtable - OB社区插件汇总 - Johnny整理 - 每周更新 - B站 Johnny学Explore the "OB社区插件汇总 - Johnny整理 - 每周更新 - B站 Johnny学&qu…

执行操作后的变量值,我的题解首次优于官方

2011. 执行操作后的变量值 难度简单46 存在一种仅支持 4 种操作和 1 个变量 X 的编程语言&#xff1a; X 和 X 使变量 X 的值 加 1--X 和 X-- 使变量 X 的值 减 1 最初&#xff0c;X 的值是 0 给你一个字符串数组 operations &#xff0c;这是由操作组成的一个列表&#xf…

求N阶矩阵的幂(一维,二维多种方法)

引用&#xff1a;对于矩阵的计算想必都是一件很头疼的事情吧&#xff0c;因为计算量是比较大&#xff0c;因为你要用前一个矩阵的行乘以后矩阵的列且对应相加才得到新矩阵的第一个元素&#xff0c;且两个矩阵可以相乘的条件也是前一个矩阵的列等于后一个矩阵的行&#xff0c;操…

Simulink代码生成: Switch模块及其代码

本文描述Switch模块的建模并研究生成的代码。 文章目录1 Simulink中的Switch模块2 Switch模块建模及代码生成3 Switch模块其他用法3.1 多重Switch3.2 通过标定量Switch4 总结1 Simulink中的Switch模块 在Simulink中Switch模块时非常常见的&#xff0c;通常用于根据一定地条件选…

Python学习笔记(十九)——Matplotlib入门上

目录 Matplotlib简介 导入matplotlib模块 图的参数说明 matplotlib图像组成部分介绍 matplotlib绘图步骤分析 matplotlib实现简单图像 matplotlib画布 画布-plt.figure() 实例 同一画布制作多张图像 创建多个子图 实例 plt.subplots 相关参数 调整subplot周围的间距…

使用HGS算法调整PD控制器增益的无人机动态性能数据——基于启发式的无人机路径跟踪优化(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

【Sentinel 预热加载】

系列文章目录 Sentinel 预热加载 目录 系列文章目录 前言 一、概念解释&#xff1f; 二、使用步骤 1.引入库 2.dashboard 配置 总结 前言 一、概念解释&#xff1f; Warm Up&#xff1a;根据coldFactor&#xff08;冷加载因子&#xff0c;默认3&#xff09;的值&#xff0c;从…

有了 HTTP,为什么还要 RPC?

RPC主要是基于TCP/IP协议的&#xff0c;而HTTP服务主要是基于HTTP协议的&#xff0c;我们都知道HTTP协议是在传输层协议TCP之上的&#xff0c;所以效率来看的话&#xff0c;RPC当然是要更胜一筹啦&#xff01;下面来具体说一说RPC服务和HTTP服务。 OSI网络七层模型 在说RPC和…

Android HIDL和hwservicemanager

HIDL软件包 HIDL 接口软件包位于 hardware/interfaces 或 vendor/ 目录下&#xff08;个别情况除外&#xff09;。顶层 hardware/interfaces 会直接映射到 android.hardware 软件包命名空间&#xff1b;版本是软件包&#xff08;而不是接口&#xff09;命名空间下的子目录。 h…

ALPHA项目的测试电机、性能信息和动态推力近似值数据库(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 按照电机试验的完整性&#xff0c;可以将电机试验分为型式试验、单项目或部分项目试验等&#xff1b;其中型式试验包括产品的性…

【深入浅出Spring原理及实战】「开发实战系列」SpringSecurity与JWT实现权限管控以及登录认证指南

SpringSecurity介绍 SpringSecurity是一个用于Java 企业级应用程序的安全框架&#xff0c;主要包含用户认证和用户授权两个方面&#xff0c;相比较Shiro而言&#xff0c;Security功能更加的强大&#xff0c;它可以很容易地扩展以满足更多安全控制方面的需求&#xff0c;但也相…

刷爆力扣之重复叠加字符串匹配

刷爆力扣之重复叠加字符串匹配 HELLO&#xff0c;各位看官大大好&#xff0c;我是阿呆 &#x1f648;&#x1f648;&#x1f648; 今天阿呆继续记录下力扣刷题过程&#xff0c;收录在专栏算法中 &#x1f61c;&#x1f61c;&#x1f61c; 该专栏按照不同类别标签进行刷题&…