6\8. 语义分析和中间代码生成

news2024/9/24 8:34:24

6&8. 语义分析和中间代码生成

从几个问题说起:

要计算 3+4*5 的值如何计算?按照以前的方法,我们会想到利用符号栈和数值栈来完成这个运算。但是有了语法分析之后我们不再需要进行这么复杂的代码构造了。第一,我们可以使用自顶而下的递归下降分析方法;第二,我们可以使用自底而上的LR分析方法;而这一切的前提都是我们有能力将一些问题改成形式化的描述,即构造其文法;再使用语法分析的方法,在语法分析的基础上,再特定位置加上相应的处理功能——语义动作。

语法制导翻译(具体例子在下文)

所谓语法制导翻译就是在解析输入的字符串的时候,在特定位置执行指定的动作;换言之,根据语法把输入的字符串”翻译“成一串动作。方法有四种:

  • 方法一:在确定的递归下降语法分析程序中,利用隐含堆栈存储各递归下降函数内的局部变量所表示的语义信息。

  • 方法二:在自底向上语法分析程序中使用和语法分析栈同步操作的语义栈进行语法分析翻译**(实际上是每个规则都有对应的语义动作)**

  • 方法三:在LL(1)语法分析程序中,利用翻译文法实施语法分析翻译

    • 翻译文法是在描述语言的文法(即源文法或输入文法)加入语义动作符号而形成的。
  • 方法四:利用属性文法进行语法分析翻译。

    • 属性文法也是一种翻译文法
    • 其符号(文法符号和动作符号)都扩展为带有语义属性和同一规则内各属性间的运算规则。(一般的程序设计语言都必须有,因为要同一类型的操作数才能运算)

有关语义分析的基本概念

语法分析的实施

  • 语法分析的时候直接调用语义分析函数进行语义分析(语法制导翻译);
  • 先生成相应的语法树,再做语义分析;

语义分析通常包括两个方面

  • 检查语法结构的静态语义,通常是分析句子的作用域和类型;
  • 对合乎语义的句子进行翻译,并生成某种中间形式(树、后缀表示、三元组、四元组、P代码);

语句的种类

  • 说明语句:用于定义各种名字的属性;
  • 执行语句:用于完成指定功能;

语义分析的种类

  • 分析说明语句:需要把名字的各种属性登记到符号表,以便在分析的时候使用到;
  • 分析执行语句:通过分析该语句生成相应的中间代码;

语义分析的例题(2018级考试题)

题目:小明发明了一种新的类型声明语句,形式如下,请你写出其对应的文法及语义分析程序。

i:float
i,j,k:int

文法规则:

S->V

V->i,V | i :T

T->int |float|double|char

ep 2.设计语法分析程序

void V()
{
   match(token);
  
   if(token==',')
   {
       match(token);
       V();
   }
   else if(token==':')
   {
       match(token);
       T();
     
   }
   else
   {
       error();
   } 
}
void T()
{
    if(token == "int"||token=="float"||token=="char")
    {
        match(token);
    }
    else
    {
        error();
    }
}

ep 3.添加语义动作

void V()//V->  i [  ,V(1)  | :T  ]
{
   match(i);
   enter(i);
   if(token==',')
   {
       match(token);
       V();
       i.type=V(1).type;
   }
   else if(token==':')
   {
       match(token);
       T();
       Fill(entry(i),T.type);
       V.type=T.type;
   }
   else
   {
       error();
   } 
}
void T()
{
    if(token == "int"||token=="float"||token=="char")
    {
        match(token);
        T.type=token;
    }
    else
    {
        error();
    }
}

在这里插入图片描述

常见的中间代码形式

中间代码是一种结构简单、含义明确的记号系统,复杂性介于源代码和机器代码之间,容易将其翻译成目标代码。

形式:

  • 后缀表示
  • 三元组
  • 四元组
  • P代码

如何将一个算数表达式转换为逆波兰表示(后缀表达式)、四元组表示、三元组表示

课件以逆波兰表示为例子解释。

自顶而下递归下降

Step 1.构造文法

为了简单考虑,把算数表达式简化为如下的简单文法
在这里插入图片描述

这里需不需要提取左公因子和消除左递归,在于选用什么样的方法做语义分析,可以是个自顶而下的递归下降、LL(1)分析方法,也可以是自底而上的LR分析方法等。这里选择用自顶而下的方法,则必须提取左公因子和消除左递归。改造后的文法如下:
在这里插入图片描述

Step 2.设计语法分析程序

void E()
{
    T();
    while(token == '+')
    {
        match(token);
        T();
    }
}
void T()
{
    F();
    while(token == '*')
    {
        match(token);
        F();
    }
}
void F()
{
    if(token == '(')
    {
        match('(');
        E();
        match(')');
    }
    else if(token == n)
    {
        match(token);
    }
    else
    {
        Error();
    }
}

Step 3. 添加语义动作

void E()
{
    T();
    while(token == '+')
    {
        match(token);
        T();
        ans[pos++] = '+';//语义动作
    }
}
void T()
{
    F();
    while(token == '*')
    {
        match(token);
        F();
        ans[pos++] = '*';//语义动作
    }
}
void F()
{
    if(token == '(')
    {
        match('(');
        E();
        match(')');
    }
    else if(token == n)
    {
        match(token);
        ans[pos++] = n;//语义动作
        ans[pos++] = '@';//语义动作
    }
    else
    {
        Error();
    }
}

自底而上LR分析方法

画出DFA图之后,很容易发现存在移进-归约冲突,不属于LR(0)文法,尝试使用SLR(1),可以解决,因此,是SLR(1)文法。画出SLR(1)分析表,在归约的地方写语义动作。(实际上每一条规则都会有其语义动作,只不过有可能是空)

语义动作

  • E → E + T

    //转后缀表达式
    {
        post[p++] = '+';
    }
    
  • E → T

    
    
  • T → T * F

    //转后缀表达式
    {
        post[p++] = '*';
    }
    
  • T → F

    
    
  • F → (E)

    
    
  • F → n

    //转后缀表达式
    {
        pos[p++] = n;
    }
    

至于为什么在3归约到E的时候继续读取+,是因为DFA图决定的,又或者说文法决定的。本质上是遇到了+需要移进。以上还可以改成求其结果。

如何将一段代码翻译成中间代码(后缀、三元组、四元组)(主要是四元组)

扩充后缀表达式

  • 算数后缀表达式

  • 数组运算后缀表达式

    a[e] → e a SUBS

    其中SUBS表示数组下标运算

  • 条件语句后缀表达式

    if ( u ) S1 else S2 → u L1 BZ S1 L2 BR S2

    • BZ双目运算符,表示当u不成立(为零)时转向标号L1部分继续执行

    • L1表示语句S2开始执行的位置

    • BR为一个单目运算符,表示无条件转向L2部分继续执行

    • L2表示该条件语句下一个语句开始执行的位置

  • while语句后缀表达式

    while (u) s →

    L1

    ​ u L2 BZ s L1 BR

    L2

    跳转再执行语句变成执行语句再跳转

  • repeat语句后缀表达式

    repeat s until u →

    L1

    ​ s u L2 BZ L1 BR

    L2

本质上代码的后缀表达式是根据汇编代码改变来的,遵循逆波兰表示法原则:运算对象顺序不变;符号在后;

以if来看:

    u
    BZ L1
    S1
    BR L2
L1: S2
L2:...

以逆波兰表示原则,则 u L1 BZ S1 L2 BR S2

四元组的表示方法

四元组的表示方法

(OP , P1 , P2 , T)

其中OP为运算符,OP1、OP2 为运算对象,T为计算结果的临时暂存变量。

( + , b , c , T1)

(*, a , T1, T2)

扩充表示

**赋值语句 a=e **

先将e转换为四元组,在将 = 运算转换为四元组。

如a=b+c,则四元组表示为:

(+, b , c , T1)

(= ,T1 , ,a)

关系比较语句

a rop b ( rop 表示< 、> 、<>、>=、<=、=等关系比较运算符)

按照汇编的生成就行(J, , , ?)无条件跳转到?的位置

理解课件中逻辑表达式、if语句和while语句需要做文法分割的地方。分割的意义是为了能够尽早地做语义动作,不要拖到后面一个大的归约项再做。

  • 如何判断在哪里分割文法?
    1. 可以选择先转成汇编代码,需要标号的地方必定是需要分割的。
    2. 根据经验总结判断,(E) 的 ) 处需要分割、逻辑表达式的 &&|| 需要分割、if语句的 else 需要分割、while语句的 while 需要分割、repeat语句的 repeatuntil 需要分割,其中until处需要改变跳转的方向、dowhile语句的 dowhile 需要分割,其中while处需要决定跳转的标号。
  • 如何判断要做怎样的语义动作?
    1. 可以先画出语句归约的路线,通过经验或者汇编代码知道哪里需要传递?传递什么?如何传递?以及不是每一个东西都需要传递。

注意:大部分只有一个链的非终结符,如:S、C、T等,他们都是带着FC的出口,不会带着TC的出口,因为TC大概率都会在归约到C、T时填入。而BT、BE、BF都是有自己的两个出口。

递归下降+语义动作练习:

在这里插入图片描述

下面一些题目是根据课件给出的代码语句题目的练习,在课件中用的是LR方法+语义动作,下面将利用递归下降+语义动作的练习。并不是说只有LR方法才需要切割文法,递归下降也需要隐式地做切割,在切割处加上语义动作。

题目1: if文法用递归下降生成四元组(这只是核心,S还有别的规则才能终止,并且exp也有一套文法)

step 1. 文法如下:
在这里插入图片描述

step 2. 递归下降

void S()
{
    match(IF);
    match('(');
    exp();
    match(')');
    S();
    if(token == ELSE)
    {
        match(token);
        S();
    }
}

step 3. 加语义动作

ListNode* S()
{
    ListNode* s;
    ListNode* p1;
    ListNode* p2;
    ListNode* p3;
    ListNode* t;
    ListNode* c;
    match(IF);
    match('(');
    p1 = exp();
    match(')');

    //-----对应C->if(exp)的语义动作------
    BackPatch(p1.TC, nextStat);
    c = new ListNode();
    c.chain = p1.FC;

    p2 = S();

    if(token == ELSE)
    {
        match(token);

        //-----对应T->CS else的语义动作------
           int q = nextStat;
        Gen(j,_,_,0);
        BackPatch(c.chain, nextStat);
        t = new ListNode();
        t.chain = merge(p2.chain, q);

        p3 = S();

        //--------对应S->TS的语义动作------------
        s = new ListNode();
        s.chain = merge(t.chain, p3.chain);
    }
    else
    {
        //-------对应S->CS的语义动作-------
        s = new ListNode();
        s.chain = merge(c.chain, p2.chain);
    }
    return s;
}

题目2: while文法用递归下降生成四元组(这只是核心,S还有别的规则才能终止,并且exp也有一套文法)

step 1. 文法如下:
在这里插入图片描述
step 2. 递归下降

void S()
{
    match(WHILE);
    match('(');
    exp();
    match(')');
    S();
}

step 3. 加语义动作

ListNode* S()
{
    ListNode* s,* e;ListNode* w;
    ListNode* s1;
    match(WHILE);

    //---W->while----
    int head = nextStat;

    match('(');
    e = exp();
    match(')');

    //----Wd->W(exp)-----
    BackPatch(e.TC, nextStat);
    w = new ListNode();
    w.chain = e.FC;
    w.head = head;

    s1 = S();

    //------S->Wd S-------
    s = new ListNode();
    BackPatch(s1.chain, w.head);
    Gen(j,_,_,w.head);
    s.chain = w.chain;

    return s;
}

题目3: repeat文法用递归下降生成四元组(这只是核心,S还有别的规则才能终止,并且exp也有一套文法)

step 1. 文法如下:
在这里插入图片描述
通过快速顺一遍发现需要回到repeat的位置,所以repeat处要分割;until处要把S的假出口链回填,所以也要分割。(exp)的 ) 按照if和while的经验可知需要分割出真假出口。

step 2. 递归下降

void S()
{
    match(REPEAT);
    S();
    match(UNTIL)
    match('(');
    exp();
    match(')');
}

step 3. 加语义动作

ListNode* S()
{
    ListNode* s;
    ListNode* s1;
    ListNode* e;

    match(REPEAT);
    //-----R->repeat-----
    int head = nextStat;

    s1 = S();
    match(UNTIL)
    //------Ru->RS until-----
    BackPatch(s1.chain, nextStat);

    match('(');
    e = exp();
    match(')');
    //-------S->Ru (exp)--------
    BackPatch(e.FC, head);
    s = new ListNode();
    s.chain = e.TC;

    return s;
}

题目4: do…while文法用递归下降生成四元组(这只是核心,S还有别的规则才能终止,并且exp也有一套文法)

step 1. 文法如下:
在这里插入图片描述
和repeat几乎相同,只是需要改变while的真假出口的四元组。
step 2. 递归下降

void S()
{
    match(DO);
    S();
    match(WHILE);
    match('(');
    exp();
    match(')');
}

step 3. 加语义动作

ListNode* S()
{
    ListNode* s;
    ListNode* s1;
    ListNode* e;

    match(DO);
    //-----D->do-----
    int head = nextStat;

    s1 = S();
    match(WHILE);
    //------DW->do S while-----
    BackPatch(s1.chain, nextStat);

    match('(');
    e = exp();
    match(')');
    //-------S->DW (exp)--------
    BackPatch(e.TC, head);
    s = new ListNode();
    s.chain = e.FC;

    return s;
}

题目5: for文法用递归下降生成四元组(这只是核心,S还有别的规则才能终止,并且exp也有一套文法)

step 1. 文法如下:
在这里插入图片描述
exp1;处需要分割,记录exp2的开始位置,为了后面进行循环的时候回到此处。exp2;需要判断是否符合条件,要传递真出口链和假出口链和和记录exp3的开始位置;) 要生成一个跳转回exp2的开始位置的无条件跳转,回填exp2的真出口,传递exp2的假出口链和exp3的开始位置;最后的S时,要生成一个跳转回exp3的开始位置的无条件跳转,回填第二个S的假出口为exp3的开始位置,传递exp2的假出口链。

step 2. 递归下降

void S()
{
    match(FOR);
    match('(');
    S();
    match(';');
    int head= nextstat;
    t= exp();
    match(';');
    f.fc=t.fc;
    f.tc=t.tc;
    
    
    exp();
    match(')');
    
    
    S();
}

step 3. 加语义动作

ListNode* S()
{
    ListNode* s;
    ListNode* e1;
    ListNode* e2;
    ListNode* e3;
    ListNode* f;
    ListNode* fd;
    ListNode* s1;

    match(FOR);
    match('(');
    e1 = S();
    match(';');
    //----FF->for(exp;-------
    int head = nextStat;
    //int head = nextStat;

    e2 = exp();
    match(';');
    //----F->FF exp;-------
    int head2 = nextStat;
    f = new ListNode();
    f.TC = e2.TC;
    f.FC = e2.FC;
    f.head = head;
    f.head2 = head2;

    e3 = exp();
    match(')');
    //-----Fd->F exp)------
    fd.head2 = f.head2;
    fd.chain = f.FC;
    Gen(j,_,_,f.head);
    BackPatch(f.TC, nextStat);

    s1 = S();
    //-----S->Fd S---------
    Gen(j,_,_,fd.head2);
    s = new ListNode();
    s.chain = fd.chain;
    BackPatch(s1.chian, fd.head2);

    return s;
}

题目6: 赋值语句的文法

step 1. 文法如下:
在这里插入图片描述
step 2. 递归下降

void S()
{
    if(first(T).find(token) != first(T).end())
    {
        T();
    }
    match(ID);
    match('=');
    exp();
    match(';');
}
void T()
{
    switch(token)
    {
        case INT:
            match(token);
            break;
        case FLOAT:
            match(token);
            break;
        case DOUBLE:
            match(token);
            break;
        case CHAR:
            match(token);
            break;
        default:
            Error();
            break;
    }
}

step 3. 加语义动作

ListNode* S()
{
    ListNode* s;
    ListNode* t;
    ListNode* e;
    if(first(T).find(token) != first(T).end())
    {
        t = T();
        int i = entry(ID);
        fill(i, t.type);
        match(ID);
    }
    int i = entry(ID);
    match(ID);
    match('=');
    e = exp();
    match(';');
    Gen('=',i,_,e.val);
    return s;
}
ListNode* T()
{
    ListNode* t;
    switch(token)
    {
        case INT:
            t.type = token;
            match(token);
            break;
        case FLOAT:
            t.type = token;
            match(token);
            break;
        case DOUBLE:
            t.type = token;
            match(token);
            break;
        case CHAR:
            t.type = token;
            match(token);
            break;
        default:
            Error();
            break;
    }
    return t;
}

三元组的表示方法

三元组的表示方法

(OP , P1 , P2 )

其中OP为运算符,P1、P2 为运算对象.

用元组编号来来代表结果保存的位置。

对于表达式a*(b+c)对应的三元组为:

(1) ( + , b , c )

(2) (*, a , (1))

这里的(1)指的就是三元组(1)

语法制导翻译例题(2018级考试题)

六、综合分析设计题 (矩阵连乘)

当有多个矩阵进行运算时,各个矩阵之间的运算顺序可影响到最终的运算次数。如 A 为 50×20 的矩阵,B为 20×5 的矩阵,C为 5×30 的矩阵,那么 (AB)C 的运算次数为12500次,A(BC) 的运算次数为33000次,下面请你用编译原理的方法完成这一类式子的计算和判别功能,写出相关的思路和代码,要求的输入输出格式如下:

注意:不用编译原理相关的方法最多得3分!!

Input:
(ABC
(AB)C
A(BC)

Output:
No
12500
33000

Step 1. 构建文法

套用算术表达式的文法模型,进行改造。(这里直接套了没有左递归的文法规则)
在这里插入图片描述
a表示基本正则表达式,这里代表的是任意的矩阵。

如果认为空字符串不合法,这里的第一条规则应该是T{T}

Step 2. 设计语法分析程序

void E()
{
    while(token!='\0')
    {
        T();
    }
}
void T()
{
    if(token == '(')
    {
        match('(');
        E();
        match(')');
    }
    else if(token == a)
    {
        match(token);
    }
    else
    {
        Error();
    }
}
void Error()
{
    cout<<"No"<<endl;
}

Step 3. 添加语义动作并完善主程序和其他必要的元素

int ans = 0;//全局变量
struct Matrix//矩阵的结构体
{
    char name;
    int row = 0;
    int col = 0;
    Matrix(char n, int r, int c)
    {
        name = n;
        row = r;
        col = c;
    }
};

map<char, Matrix> mm;//存储字符和其规模

void match(char c)//匹配函数
{
    if(token != c)
    {
        cout<<"No"<<endl;
    }
    token = getToken();//不是语法分析程序中的函数,功能是获取下一个字符
}

Matrix* E()
{
    Matrix* m = new Matrix('@', 0, 0);
    while(token!='\0' && token != ')')
    {
        Matrix* p = T();

        //-----------------语义动作--------------
        ans += m.row * m.col * p.col;
        if(m.row = 0)    m.row = p.row;
        m.col = p.col;
        //--------------------------------------
    }
    return m;
}
Matrix* T()
{
    Matrix* m;
    if(token == '(')
    {
        match('(');
        m = E();
        match(')');
    }
    else if(token == a)
    {
        match(token);
        m = mm[a];//语义动作
    }
    else
    {
        Error();
    }
    return m;
}
void Error()
{
    cout<<"No"<<endl;
}

void main()
{
    getData();//预处理,获取矩阵的名称、行和列,这里没有实现,具体要看输入行和列时是怎么样的
    getToken();//不是语法分析程序中的函数,功能是获取下一个字符
    Matrix* m = E();//实际上这个m用不上
    cout<<ans<<endl;//输出结果
}

复习课例题

2进制数转10进制数

Step 1. 构建文法

在这里插入图片描述

Step 2. 设计语法分析程序

void E()
{
    if(token == 0 || token == 1)
    {
        D();
        E(); 
    }
    else if(token == EOF)
    {

    }
    else
    {
        Error();
    }
}
void D()
{
    if(token == '0' || token == '1')
    {
        match(token);
    }
    else
    {
        Error();
    }
}

Step 3. 添加语义动作并完善主程序和其他必要的元素

int E()
{
    int temp = 0;
    if(token == 0 || token == 1)
    {
        int d = D();
        int e = E();
        temp = d*2 + e;
    }
    else if(token == EOF)
    {

    }
    else
    {
        Error();
    }
    return temp;
}
int D()
{
    int temp;
    if(token == '0' || token == '1')
    {
        match(token);
        temp = atoi(token);
    }
    else
    {
        Error();
    }
    return temp;
}

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

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

相关文章

AcWing1206.剪格子——学习笔记(未AC)

题目&#xff1a;1206. 剪格子 - AcWing题库https://www.acwing.com/problem/content/description/1208/ import java.util.Scanner;public class Main {public static void main(String args[]){//读数据Scanner input new Scanner(System.in);String MN input.nextLine();S…

Apache ShardingSphere分表的简单使用和常见问题

目录 简介 什么是 Apache ShardingSphere? 分库分表的背景 使用 pom 配置 1&#xff0c;application.properties配置文件 2&#xff0c;创建配置类 分表 验证分表 常见问题 自定义分表规则未生效 简介 官网&#xff1a;Apache ShardingSphere 版本&#xff1a;4…

ArcGIS基础实验操作100例--实验42创建渔网Fishnet

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验42 创建渔网Fishnet 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&a…

深度学习推理框架调研总结

深度学习推理框架1.移动端深度学习推理框架调研1.1 小米的MACE&#xff08;2017&#xff09;1.2 阿里的MNN1.3 腾讯的TNN1.4 ARM的tengine1.5 百度的paddle-mobie1.6 Facebook的Caffe2&#xff08;*&#xff09;1.7 Google的TensorFlow Lite &#xff08;*&#xff09;1.8 Appl…

南京晓庄操作系统期末复习【背诵部分】

操作系统期末复习背诵部分第一章1.操作系统的特征&#xff1a;2.操作系统的最基本特征是什么&#xff0c;为什么3.操作系统的目标4.操作系统的主要功能5.计算机资源6.多道程序设计时应解决什么问题7.三种操作系统及特点第二章1.进程的定义2.进程的特征3.进程的三种基本状态4.进…

Spark04: Transformation与Action开发

一、创建RDD的三种方式 RDD是Spark编程的核心&#xff0c;在进行Spark编程时&#xff0c;首要任务是创建一个初始的RDD这样就相当于设置了Spark应用程序的输入源数据然后在创建了初始的RDD之后&#xff0c;才可以通过Spark 提供的一些高阶函数&#xff0c;对这个RDD进行操作&a…

python学生管理系统(pyqt5 含界面)

学生管理系统项目流程 项目模块 账号登陆 人脸识别 增添学生信息 删除学生信息 改动学生信息 查询学生信息 项目主体框架 app.py为主代码&#xff0c;负责用于界面打开展示。img文件夹负责放置项目qrc的图像page文件夹为单独页面的类plugin文件夹为功能模块的类ui文件夹…

机器人中的数值优化|【一】数值优化基础

数值优化基础 凸集 Convex Sets 凸集的定义 令X是线性空间。如果对于X的子集S中的所有x和y&#xff0c;并且在区间 [0,1]中的所有t&#xff0c;点 (1−t)xty(1-t)x ty(1−t)xty也属于S&#xff0c;则S称为凸集。 不失一般性&#xff0c;对于所有的凸集&#xff0c;其线性组…

Zookeeper详解(二)——API 事件监听

Java API znode是zooKeeper集合的核心组件&#xff0c;zookeeper API提供了一小组方法使用zookeeper集合来操纵znode的所有细节。 客户端应该遵循以下步骤&#xff0c;与zookeeper服务器进行清晰和干净的交互。 连接到zookeeper服务器。zookeeper服务器为客户端分配会话ID。…

uniapp 之 接入小程序客服

目录 前言 小程序客服 代码只需要一步 配置也需要一步​​​​​​​ 前言 小程序客服 因老大 看到别人家有在线客服这个功能&#xff0c;就让我也做一个&#xff0c;这个功能很简单 效果图1 代码只需要一步 <button type"default" open-type"con…

MATLAB | 绘图复刻(六) | 分组环形热图

有粉丝问我Ecology Letters, (2021) 24: 1018–1028 Soil carbon persistence governed by plant input and mineral protection at regional and global scales 这篇文章中的Figure 2咋画&#xff0c;原图长这样&#xff1a; 复刻效果&#xff1a; 完整步骤 0 数据定义 按…

node.js创建网站实例1

1.node.js安装 我的电脑环境&#xff1a;win10 网址&#xff1a;https://nodejs.org/en/ 我下载了18.12.1版本 一路next默认安装&#xff0c;安装完成后&#xff0c;运行cmd&#xff0c;查看版本号 会同时安装npm&#xff0c;也可以同时查看版本号 2.创建第一个网站实例hell…

内卷对于2022是一种无奈,也是一种修行

其实我们谁也不知道2023年对于我们普通的开发人员来说会有什么样的试炼&#xff0c;因为2022年身边有太多的人&#xff0c;为了工作&#xff0c;为了生活&#xff0c;为了家庭&#xff0c;为了理想&#xff0c;不得不选择走向别人看似很卷的那条路。 对于我们周围的人来说&…

【Vim】基本操作及命令集详解

概述 Vim 是从 vi 发展出来的一个文本编辑器。vi 内置在Linux系统中&#xff0c;是vim的简化版编辑器&#xff0c;vim则需要进行安装使用。Vim代码补全、编译及错误跳转等方便编程的功能特别丰富&#xff0c;可以实现高效率移动和高效的输入&#xff0c;在程序员中被广泛使用。…

CPT203-Software Engineering(3)

文章目录9. Software Design9.1 Architecture Design9.1.1 Architectural patterns9.2 Component-level Design9.2.1 Component9.2.2 Views of component9.2.3 Component-level design process9.3 User Interface Design9.3.1 Interface Design Process9.3.2 Interface Design …

蓝桥杯Python练习题16-最大最小公倍数

资源限制   内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述   已知一个正整数N&#xff0c;问从1~N中任选出三个数&#xff0c;他们的最小公倍数最大可以为多少。 输入格式   输入一…

三维数学(一)

视频教程&#xff1a;https://www.bilibili.com/video/BV12s411g7gU?p155 向量 一个数字列表&#xff0c;表示各个维度上的有向位移&#xff1b;同时也是一个有大小有方向的物理量&#xff0c;大小及向量的模长&#xff0c;而方向即空间中向量的指向&#xff0c;可以表示物体…

TikTok Shop 越南站点收入已达Lazada 的 80%

让我们一起来看看今日都有哪些新鲜事吧&#xff01;01 TikTok Shop 越南站点收入已达Lazada 的 80% 据越南电商平台数据分析软件Metric.vn 统计&#xff0c;Shopee、Lazada、Tiki 和 Sendo 仍然主导着越南电子商务市场&#xff0c;1-11 月&#xff0c;共销售了 13 亿件产品。其…

简化开发小技巧-Mybatis-Plus的使用和常用操作

目录 简介 快速使用 pom 代码 mapper service 使用 常用操作 简单或操作查询 多条件或查询 更新字段为null 方法一&#xff0c;如果要更新的字段是String类型&#xff0c; 方法二&#xff0c; 使用mybatis-plus的字段注入。 方法三&#xff0c;使用UpdateWrapper…

基于R的Bilibili视频数据建模及分析——预处理篇

基于R的Bilibili视频数据建模及分析——预处理篇 文章目录基于R的Bilibili视频数据建模及分析——预处理篇0、写在前面1、项目介绍1.1 项目背景1.2 数据来源1.3 数据集展示2、数据预处理2.1 删除空数据2.2 增加id字段2.3 处理数值字段3、参考资料0、写在前面 实验环境 Python版…