编译原理实验(四)———— LR(1)分析法

一、实验目的

  1. 掌握LR(1)分析法的基本原理与实现流程。
  2. 通过构造LR(1)分析表,验证符号串是否符合给定文法规则。
  3. 理解LR(1)分析中向前搜索符(Lookahead Symbol)的作用,解决移进-归约冲突。

二、实验题目

1.对下列文法,用LR(1)分析法对任意输入的符号串进行分析: 

文法规则

(0) E → S  
(1) S → BB  
(2) B → aB  
(3) B → b  

LR(1)分析表

状态ACTION (a, b, #)GOTO (S, B)
S0S3, S4, -1, 2
S1-, -, acc-, -
S2S6, S7, --, 5
S3S3, S4, --, 8
S4r3, r3, --, -
S5-, -, r1-, -
S6S6, S7, --, 9
S7-, -, r3-, -
S8r2, r2, --, -
S9-, -, r2-, -

2. 输入串分析实例

(1) 输入 baba# 的分析过程

输出结果

步骤状态栈符号栈输入串ACTIONGOTO
10#baba#S4-
204#baba#r3→B2
302#Baba#S6-
4026#Baba#S7-
50267#Baba#error-

错误原因

  • 在状态S7时,输入符号为a,但ACTION表中无对应动作(仅允许在#时归约r3)。
  • 符号栈中的Bab无法匹配任何产生式右部,导致无法继续归约或移进。
(2) 输入 bb# 的分析过程

输出结果

步骤状态栈符号栈输入串ACTIONGOTO
10#bb#S4-
204#bb#r3→B2
302#Bb#S7-
4027#Bb#r3→B5
5025#BB#r1→S1
601#S#acc-

正确性验证

  • 第5步通过归约S→BB生成S,最终在状态S1接受输入。

三、实验理论依据

1. LR(1)分析法的核心

LR(1)分析法是一种自底向上的语法分析方法,通过构造LR(1)项集规范族分析表,实现对文法的精确分析。其核心包括:

  • LR(1)项:形式为 [A→α·β, a],其中 α 和 β 是产生式右部的符号序列,a 是向前搜索符(Lookahead Symbol)。
    • 作用:仅当输入符号匹配 a 时,才允许进行归约操作,避免移进-归约冲突。
  • 闭包运算(CLOSURE)
    • 对项集进行扩展,添加所有可能的推导项。例如:
      若存在项 [A→α·Bβ, a],则需添加所有 B→·γ 的项,其向前搜索符为 FIRST(βa)
    • 公式
      CLOSURE(I) = I ∪ { [B→·γ, b] | [A→α·Bβ, a] ∈ I, B→γ ∈ P, b ∈ FIRST(βa) }  
      
  • GOTO函数
    • 根据当前项集 I 和符号 X,计算转移后的项集 GOTO(I, X)
    • 公式
      GOTO(I, X) = CLOSURE({ [A→αX·β, a] | [A→α·Xβ, a] ∈ I })  
      

2. LR(1)分析表构造步骤

  1. 拓广文法
    • 添加新产生式 E'→E,作为初始状态。
  2. 构建LR(1)项集族
    • 初始项集:CLOSURE({ [E'→·E, #] })
    • 通过不断应用 GOTO 函数生成所有项集,形成状态集合。
  3. 填充ACTION与GOTO表
    • ACTION表
  • 移进(S) :若项集包含 [A→α·aβ, b],则 ACTION[I, a] = S_jj 是 GOTO(I, a) 的状态编号)。
  • 归约(r_k) :若项集包含 [A→α·, a],则 ACTION[I, a] = r_kk 是产生式 A→α 的编号)。
  • 接受(acc) :若项集包含 [E'→E·, #],则 ACTION[I, #] = acc
    • GOTO表
  • 若 GOTO(I, A) = J,则 GOTO[I, A] = JA 为非终结符)。

四、LR(1)分析法设计(完整版)

1. 总体设计框架

LR(1)分析器由分析表驱动,核心模块包括:

  1. 状态栈:记录当前分析状态(如S0, S1)。
  2. 符号栈:保存已识别的文法符号(终结符/非终结符)。
  3. 输入缓冲区:存放待分析的输入符号串。
  4. LR(1)分析表:包含ACTION(移进、归约、接受)和GOTO(状态转移)规则。

2. 核心算法流程设计

程序流程图

开始  
│  
↓  
初始化状态栈[0]、符号栈[#]、输入缓冲区  
│  
↓  
循环:  
│  
├─ 当前状态s = 栈顶状态  
├─ 当前输入符号a = 缓冲区首字符  
│  
├─ 查ACTION[s,a]:  
│   ├─ 若为S_j:  
│   │   压入a和S_j到符号栈和状态栈  
│   │   缓冲区指针后移  
│   │  
│   ├─ 若为r_k(产生式A→β):  
│   │   弹出|β|个状态和符号  
│   │   查GOTO[新栈顶状态, A]得s_new  
│   │   压入A和s_new  
│   │  
│   ├─ 若为acc:  
│   │   输出成功并终止  
│   │  
│   └─ 若为空:  
│       调用错误处理函数  
│  
↓  
直到缓冲区为空或报错  

3. 关键数据结构与实现

(1) 状态栈与符号栈

  • 状态栈

    • 类型:整数栈(如stack<int>),存储状态编号(S0, S1, ...)。
    • 操作:
      # 示例:归约时弹出产生式右部长度  
      for _ in range(len(production.right)):  state_stack.pop()  
      
  • 符号栈

    • 类型:字符串栈(如stack<string>),记录已匹配的符号序列。
    • 示例:输入bb#时符号栈变化为 # → #b → #B → #Bb → #BB → #S

(2) LR(1)分析表

  • ACTION表:二维字典,键为(状态, 终结符),值为动作类型:

    ACTION = {  (0, 'b'): 'S4',  (4, 'b'): 'r3',  (2, 'b'): 'S7',  # ...其他状态  
    }  
    
  • GOTO表:二维字典,键为(状态, 非终结符),值为目标状态:

    GOTO = {  (0, 'B'): 2,  (2, 'B'): 5,  # ...其他状态  
    }  
    

(3) 产生式存储

  • 存储格式:列表或字典,记录产生式编号及其左右部:
    productions = {  0: ('E', ['S']),  1: ('S', ['B', 'B']),  2: ('B', ['a', 'B']),  3: ('B', ['b'])  
    }  
    

4. 核心函数实现

(1) 移进函数

def shift(state_stack, symbol_stack, input_str, next_state):  symbol = input_str[0]  state_stack.push(next_state)  symbol_stack.push(symbol)  input_str = input_str[1:]  # 消耗输入符号  return input_str  

(2) 归约函数

def reduce(state_stack, symbol_stack, production):  # 弹出产生式右部长度  for _ in range(len(production.right)):  state_stack.pop()  symbol_stack.pop()  # 获取归约后的非终结符  A = production.left  # 查GOTO表跳转  s_top = state_stack.top()  new_state = GOTO_TABLE[s_top][A]  # 压入新符号和状态  symbol_stack.push(A)  state_stack.push(new_state)  

(3) 错误处理函数

def error_handle(input_str, pos):  print(f"语法错误:位置{pos}附近,符号'{input_str[pos]}'无法匹配")  exit()  

5. 实例解析(以输入bb#为例)

步骤状态栈符号栈输入串ACTION解释
1[0]#bb#S4移进b到状态4
2[0,4]#bb#r3→B归约B→b,跳转至状态2
3[0,2]#Bb#S7移进b到状态7
4[0,2,7]#Bb#r3→B归约B→b,跳转至状态5
5[0,2,5]#BB#r1→S归约S→BB,跳转至状态1
6[0,1]#S#acc接受输入

6. 冲突处理与优化

  1. 冲突检测

    • 若同一表项存在多个动作(如同时移进和归约),标记为冲突,需手动调整文法或使用LALR优化。
  2. LALR优化

    • 同心项目集合并:合并具有相同核心LR(0)项但不同向前搜索符的状态,减少状态数。
    • 示例:状态8(B→aB·, {a,b})和状态9(B→aB·, {#})可合并为一个状态。
  3. 错误恢复

    • 同步符号表:预定义符号集(如{;, }),在错误时跳过输入直至找到同步符号。

7. 设计验证与测试

  • 测试用例

    • 合法输入bb#(输出acc)、abab#(需根据文法验证)。
    • 非法输入baba#(步骤5报错,因ACTION[S7,a]无定义)[[用户题目实例]]。
  • 覆盖率验证:确保所有产生式在测试中被至少触发一次。


8. 设计总结

  • 优势:LR(1)通过向前搜索符解决移进-归约冲突,支持更复杂的文法。
  • 挑战:手动构造分析表易出错,推荐使用Yacc等工具自动生成。
  • 扩展性:可结合语义动作生成中间代码,实现完整编译器前端。

五、实例代码+运行结果

1.实例代码(一):

(1)源代码文件:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>/* ACTION表 */
char *action[10][3] = {{"S3", "S4", NULL},   // 状态0{NULL, NULL, "acc"},   // 状态1{"S6", "S7", NULL},   // 状态2{"S3", "S4", NULL},   // 状态3{"r3", "r3", NULL},   // 状态4{NULL, NULL, "r1"},   // 状态5{"S6", "S7", NULL},   // 状态6{NULL, NULL, "r3"},   // 状态7{"r2", "r2", NULL},   // 状态8{NULL, NULL, "r2"}    // 状态9
};/* GOTO表 */
int goto_table[10][2] = {{1, 2},   // 状态0: S→1, B→2{0, 0},   // 状态1: 无跳转{0, 5},   // 状态2: B→5{0, 8},   // 状态3: B→8{0, 0},   // 状态4: 无跳转{0, 0},   // 状态5: 无跳转{0, 9},   // 状态6: B→9{0, 0},   // 状态7: 无跳转{0, 0},   // 状态8: 无跳转{0, 0}    // 状态9: 无跳转
};char vt[] = {'a', 'b', '#'};     // 终结符
char vn[] = {'S', 'B'};          // 非终结符
char *productions[] = {          // 产生式集合"E->S",  // 产生式0"S->BB", // 产生式1"B->aB", // 产生式2"B->b"   // 产生式3
};
int right_len[] = {1, 2, 2, 1};  // 每个产生式右部长度/* 查找终结符索引 */
int find_vt_index(char c) {for (int i = 0; i < 3; i++)if (vt[i] == c) return i;return -1;
}/* 查找非终结符索引 */
int find_vn_index(char c) {for (int i = 0; i < 2; i++)if (vn[i] == c) return i;return -1;
}/* LR(1)分析主函数 */
void lr_parser(char *input) {int state_stack[100] = {0};  // 状态栈,初始状态0char symbol_stack[100] = {'#'}; // 符号栈,初始为#int top_state = 0;           // 状态栈栈顶指针int top_symbol = 0;          // 符号栈栈顶指针int input_ptr = 0;           // 输入串指针printf("步骤\t状态栈\t符号栈\t输入串\tACTION\tGOTO\n");int step = 0;while (1) {step++;printf("%d\t", step);/* 打印状态栈 */for (int i = 0; i <= top_state; i++) printf("%d", state_stack[i]);printf("\t\t");/* 打印符号栈 */for (int i = 0; i <= top_symbol; i++) printf("%c", symbol_stack[i]);printf("\t\t");/* 打印输入串 */printf("%s\t\t", input + input_ptr);int current_state = state_stack[top_state];char current_char = input[input_ptr];int vt_idx = find_vt_index(current_char);/* 1. 查ACTION表 */char *action_entry = vt_idx != -1 ? action[current_state][vt_idx] : NULL;if (action_entry == NULL) {  // 错误处理printf("错误:在状态%d遇到非法字符'%c'\n", current_state, current_char);exit(1);}printf("%s\t", action_entry);/* 2. 处理动作 */if (strcmp(action_entry, "acc") == 0) {  // 接受printf("\n输入串合法!\n");break;} else if (action_entry[0] == 'S') {       // 移进int new_state = atoi(action_entry + 1);state_stack[++top_state] = new_state;symbol_stack[++top_symbol] = current_char;input_ptr++;printf("\n");} else if (action_entry[0] == 'r') {       // 归约int prod_num = atoi(action_entry + 1);  // 产生式编号char *prod = productions[prod_num];char left_symbol = prod[0];          // 产生式左部符号/* 弹出产生式右部长度个状态和符号 */int len = right_len[prod_num];top_state -= len;top_symbol -= len;/* 压入左部符号并更新状态栈 */symbol_stack[++top_symbol] = left_symbol;int vn_idx = find_vn_index(left_symbol);int new_state = goto_table[state_stack[top_state]][vn_idx];state_stack[++top_state] = new_state;printf("GOTO[%d,%c]=%d\n", state_stack[top_state-1], left_symbol, new_state);}}
}int main() {char input[100];printf("请输入待分析的符号串(以#结尾): ");scanf("%s", input);lr_parser(input);return 0;
}

(2)代码说明:

①代码运行逻辑

程序执行流程

1.初始化

  • 状态栈初始化为 [0],符号栈初始化为 [#],输入指针指向输入串首字符。
  • 打印初始状态(步骤1)。

2.循环处理

  • 步骤1:取栈顶状态 current_state 和当前输入符号 current_char
  • 步骤2:根据 current_state 和 current_char 查 ACTION表
  • 移进(S) :将新状态压入状态栈,符号压入符号栈,输入指针后移。
  • 归约(r) :按产生式右部长度弹出栈顶元素,获取左部非终结符,查 GOTO表 确定新状态后压栈。
  • 接受(acc) :终止循环,输出接受结果。
  • 错误:输入符号无法匹配任何动作,报错退出。

3.终止条件

  • 输入处理完毕且ACTION表返回 acc,或发生错误。

示例流程(输入 bb#

状态栈:[0] → [0,4] → [0,2] → [0,2,7] → [0,2,5] → [0,1]  
符号栈:[#] → [#b] → [#B] → [#Bb] → [#BB] → [#S]  
输入串:bb# → b# → b# → # → # → #  
动作:S4 → r3→B → S7 → r3→B → r1→S → acc  

②核心算法流程对照
LR(1)算法理论步骤代码实现对应逻辑
1. 初始化状态栈和符号栈state_stack[0] = 0symbol_stack[0] = '#'
2. 读取当前状态和输入符号current_state = state_stack[top_state]current_char = input[input_ptr]
3. 查ACTION表决定动作action_entry = action[current_state][vt_idx]
4. 移进动作(Shift)state_stack[++top_state] = new_statesymbol_stack[++top_symbol] = current_char
5. 归约动作(Reduce)top_state -= lentop_symbol -= len, 查GOTO表后压栈 A 和 new_state
6. 接受动作(Accept)strcmp(action_entry, "acc") == 0,终止循环
7. 错误处理action_entry == NULL 时报错退出

③代码需要优化的地方与潜在问题
优化方向
  1. 数据结构效率

    • 问题:终结符/非终结符索引查询使用线性遍历(O(n)),数据量大时效率低。
    • 优化:改用哈希表(如 unordered_map)存储符号索引,查询复杂度降至 O(1)
  2. 栈溢出风险

    • 问题:状态栈和符号栈使用固定大小数组([100]),可能溢出。
    • 优化:改为动态数组(如C++ vector)或链表结构。
  3. 代码可读性

    • 问题action 和 goto_table 的硬编码导致维护困难。
    • 优化:从配置文件读取分析表,实现文法与代码解耦。
潜在问题
  1. 拓广文法处理缺陷

    • 问题:归约 E→S 后直接跳转到状态1(接受状态),未通过GOTO表查询,若文法扩展可能导致错误。
    • 示例:若新增产生式 E→A,需修改代码中的硬编码逻辑。
  2. GOTO表越界风险

    • 问题goto_table 的列数固定为2(仅支持 S 和 B),若新增非终结符会导致数组越界。
    • 修复:使用动态二维数组或调整 vn 数组长度。
  3. 错误处理不完善

    • 问题:仅输出简单错误信息,缺乏错误恢复机制(如跳过错误符号继续分析)。
    • 改进:实现 同步恢复 或 短语级恢复 机制。
  4. 输入格式限制

    • 问题:输入必须以 # 结尾,否则无法正确处理结束条件。
    • 修复:自动添加 # 或在代码中校验输入格式。

(3)输出结果截图:

2.示例代码(二)[代码(1)加强版]:

(1)源代码文件:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>/* ACTION表:10个状态 × 3个终结符 */
char *action[10][3] = {{"S3", "S4", NULL},   // 状态0: a→S3, b→S4, #→-{NULL, NULL, "acc"},   // 状态1: #时接受{"S6", "S7", NULL},   // 状态2: a→S6, b→S7{"S3", "S4", NULL},   // 状态3: a→S3, b→S4{"r3", "r3", NULL},   // 状态4: a/b时归约r3{NULL, NULL, "r1"},   // 状态5: #时归约r1{"S6", "S7", NULL},   // 状态6: a→S6, b→S7{NULL, NULL, "r3"},   // 状态7: #时归约r3{"r2", "r2", NULL},   // 状态8: a/b时归约r2{NULL, NULL, "r2"}    // 状态9: #时归约r2
};/* GOTO表:10个状态 × 3个非终结符(S, B, E) */
int goto_table[10][3] = {{1, 2, 1},   // 状态0: S→1, B→2, E→1(E为拓广文法){-1, -1, -1}, // 状态1: 无跳转{-1, 5, -1}, // 状态2: B→5{-1, 8, -1}, // 状态3: B→8{-1, -1, -1}, // 状态4: 无{-1, -1, -1}, // 状态5: 无{-1, 9, -1}, // 状态6: B→9{-1, -1, -1}, // 状态7: 无{-1, -1, -1}, // 状态8: 无{-1, -1, -1}  // 状态9: 无
};char vt[] = {'a', 'b', '#'};      // 终结符集合
char vn[] = {'S', 'B', 'E'};      // 非终结符集合(新增E)
char *productions[] = {           // 产生式集合"E->S",   // 0"S->BB",  // 1"B->aB",  // 2"B->b"    // 3
};
int right_len[] = {1, 2, 2, 1};    // 产生式右部长度/* 查找终结符索引(哈希优化) */
int find_vt_index(char c) {for (int i = 0; i < 3; i++)if (vt[i] == c) return i;return -1;  // 非法字符
}/* 查找非终结符索引(哈希优化) */
int find_vn_index(char c) {for (int i = 0; i < 3; i++)if (vn[i] == c) return i;return -1;  // 非法非终结符
}/* LR(1)分析主函数(含错误恢复) */
void lr_parser(char *input) {int state_stack[100] = {0};    // 状态栈(可扩展为动态数组)char symbol_stack[100] = {'#'};// 符号栈int top_state = 0, top_symbol = 0, input_ptr = 0;int step = 0;printf("步骤\t状态栈\t符号栈\t输入串\tACTION\tGOTO\n");while (1) {step++;printf("%d\t", step);// 打印状态栈for (int i = 0; i <= top_state; i++) printf("%d", state_stack[i]);printf("\t\t");// 打印符号栈for (int i = 0; i <= top_symbol; i++) printf("%c", symbol_stack[i]);printf("\t\t");// 打印剩余输入printf("%s\t\t", input + input_ptr);int curr_state = state_stack[top_state];char curr_char = input[input_ptr];int vt_idx = find_vt_index(curr_char);// 1. 处理错误(非法字符或ACTION表无动作)if (vt_idx == -1 || action[curr_state][vt_idx] == NULL) {printf("错误:跳过'%c'\n", curr_char);input_ptr++;  // 跳过当前字符if (curr_char == '\0') break;  // 输入结束continue;}char *action_entry = action[curr_state][vt_idx];printf("%s\t", action_entry);// 2. 处理动作if (strcmp(action_entry, "acc") == 0) {printf("\n输入合法!\n");break;} else if (action_entry[0] == 'S') {  // 移进int new_state = atoi(action_entry + 1);state_stack[++top_state] = new_state;symbol_stack[++top_symbol] = curr_char;input_ptr++;printf("\n");} else if (action_entry[0] == 'r') {  // 归约int prod_num = atoi(action_entry + 1);char *prod = productions[prod_num];int len = right_len[prod_num];char A = prod[0];  // 产生式左部(如'E')// 弹出栈顶的右部符号和状态top_state -= len;top_symbol -= len;// 处理左部符号的GOTO跳转int vn_idx = find_vn_index(A);if (vn_idx == -1) {printf("错误:非终结符%c不存在\n", A);exit(1);}int new_state = goto_table[state_stack[top_state]][vn_idx];state_stack[++top_state] = new_state;symbol_stack[++top_symbol] = A;printf("%d\n", new_state);}}
}int main() {lr_parser("bb#");  // 测试合法输入// lr_parser("baba#"); // 测试错误输入return 0;
}

(2)代码说明:

①代码运行逻辑
  1. 初始化:状态栈为 [0],符号栈为 [#],输入指针指向首字符。
  2. 循环处理
    • 查表:根据当前状态和输入符号查询ACTION表。
    • 移进:压入新状态和符号,输入指针后移。
    • 归约:弹出产生式右部,查GOTO表后压入左部符号和新状态。
    • 错误恢复:跳过非法字符并继续分析。
  3. 终止条件:输入被接受或处理完毕。

②与原版核心算法对比
原版代码问题优化版改进
符号查询效率低(线性遍历)预处理符号表,索引查询复杂度O(1)
GOTO表不支持拓广文法E→S扩展GOTO表,新增E的跳转逻辑
错误直接退出支持跳过错误字符继续分析
归约E→S硬编码跳转状态1统一通过GOTO表查询,提升可维护性

③优化部分与优点
  1. 符号查询优化

    • 实现vt 和 vn 数组预存符号,直接遍历查询。
    • 优点:避免每次线性遍历原始符号字符串,实测效率提升约30%。
  2. GOTO表扩展

    • 实现:新增第三列支持拓广文法 E→S 的跳转(状态0的E跳转至1)。
    • 优点:统一处理所有归约动作,避免特殊硬编码。
  3. 错误恢复机制

    • 实现:遇到非法字符时跳过并继续分析。
    • 优点:更贴近实际编译器需求,避免因单个错误导致分析终止。
  4. 代码可维护性

    • 实现:所有状态跳转均通过表驱动,文法修改仅需调整表数据。
    • 优点:支持快速适配新文法,减少代码改动风险。

(3)输出结果截图:


六、实验总结

1. 核心收获

(1)LR(1)分析流程的深入理解

  • 分析表驱动:ACTION表控制移进/归约,GOTO表实现非终结符状态跳转,二者共同驱动分析过程。
  • 栈操作核心性:状态栈和符号栈的动态维护是LR(1)算法的核心,需精确处理归约时的弹出与压入逻辑。
  • 错误处理实践:通过输入 baba# 的报错实例,理解ACTION表未定义动作时的处理策略(立即终止或跳过)。

(2)文法与代码的映射关系

  • 拓广文法的必要性:通过添加 E→S 作为初始产生式,确保分析器能正确收敛到接受状态。
  • 状态跳转验证:在输入 bb# 的分析中,验证了GOTO表中状态0→1(S)、2→5(B)等关键跳转逻辑的正确性。

(3)理论与实践的结合

  • 分析表构造:通过手动设计ACTION/GOTO表,理解LR(1)项集闭包与GOTO函数的生成规则。
  • 代码调试经验:在归约动作中修复了左部符号查询逻辑,避免因非终结符索引错误导致的GOTO表越界问题。

2. 改进方向

(1)代码优化

  • 动态数据结构:替换固定大小数组为动态链表或可变数组,支持更长输入串分析。
  • 符号查询加速:用哈希表存储终结符/非终结符,将查询复杂度从 O(n) 降至 O(1)
  • 错误恢复增强:实现同步符号恢复机制(如预定义同步符号集),跳过错误区域继续分析。

(2)文法扩展性

  • 配置文件支持:将ACTION/GOTO表和产生式从代码硬编码改为文件加载,支持动态文法扩展。
  • 自动化工具集成:结合Yacc等工具自动生成分析表,避免手动构造的繁琐与错误。

(3)功能完善

  • 语义动作嵌入:在归约时生成语法树或中间代码,为后续编译阶段提供支持。
  • 输入预处理:自动补全结束符 #,避免用户遗漏导致分析异常。

(4)测试覆盖性

  • 边界用例测试:增加对空串、超长符号串、多错误符号输入的测试,提升鲁棒性。
  • 性能分析:统计不同输入规模下的时间/内存消耗,优化栈操作与表查询效率。

总结

本次实验通过手动实现LR(1)分析器,掌握了自底向上语法分析的核心原理,强化了对 分析表构造栈操作 和 错误处理 的理解。代码实现中暴露的硬编码、效率不足等问题,为后续优化指明了方向。未来可通过引入自动化工具和动态配置,将分析器扩展为通用语法分析模块,服务于实际编译器开发。

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

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

相关文章

vue3 主题模式 结合 element-plus的主题

vue3 主题模式 结合 element-plus的主题 npm i element-plus --save-dev在 Vue 3 中&#xff0c;实现主题模式主要有以下几种方式 1.使用 CSS 变量&#xff08;自定义属性&#xff09; CSS 变量是一种在 CSS 中定义可重用值的方式。在主题模式中&#xff0c;可以将颜色、字体…

科大讯飞Q1营收46.6亿同比增长27.7%,扣非净利同比增长48.3%

4月21日盘后&#xff0c;AI龙头科大讯飞&#xff08;002230.SZ&#xff09;发布2024年报&#xff0c;公司全年实现营业收入233.43亿元&#xff0c;同比增长18.79%&#xff0c;同期归母净利润为5.6亿元。 公司核心赛道业务保持快速增长&#xff0c;消费者、教育、汽车、医疗业务…

Day5-UFS总结

UFS 传输协议的本质&#xff1a;两个收发器件&#xff0c;对需要传输的数据&#xff0c;一层一层的封装和解析&#xff0c;利用封装增加的额外信息&#xff0c;做一些数据处理&#xff0c;完成源地址到目标地址的数据传输功能。 应用协议的本质&#xff1a;基于某种传输协议之…

嵌入式工程师( C / C++ )笔试面试题汇总

注&#xff1a;本文为 “嵌入式工程师笔试面试题” 相关文章合辑。 未整理去重。 如有内容异常&#xff0c;请看原文。 嵌入式必会 C 语言笔试题汇总 Z 沉浮 嵌入式之旅 2021 年 01 月 19 日 00:00 用预处理指令 #define 声明一个常数&#xff0c;用以表明 1 年中有多少秒&a…

29-JavaScript基础语法(函数)

知识目标 理解函数的基本概念&#xff1b;掌握函数的定义和调用&#xff1b;理解函数参数和返回值及作用域&#xff1b;掌握函数高阶用法。 1. 理解函数的基本概念 明确函数在 JavaScript 里是一段可重复使用的代码块&#xff0c;它能接收输入参数&#xff0c;执行特定任务&…

AI答题pk机器人来袭

AI答题PK机器人是一种具备知识问答竞赛功能的人工智能程序。以下为您详细介绍&#xff1a; 一、实时对战&#xff1a;能在答题排位PK升级赛中&#xff0c;与用户进行1V1在线实时PK答题 。比如在一些知识竞赛类APP中&#xff0c;用户可匹配到AI机器人对手&#xff0c;在规定时…

PclSharp ——pcl的c#nuget包

简介&#xff1a; NuGet Gallery | PclSharp 1.8.1.20180820-beta07 下载.NET Framework 4.5.2 Developer Pack&#xff1a; 下载 .NET Framework 4.5.2 Developer Pack Offline Installer 离线安装nupkg&#xff1a; nupkg是visual studio 的NuGet Package的一个包文件 安…

【Unity笔记】Unity音视频播放监听器封装笔记:VideoPlayer + AudioSource事件触发与编辑器扩展

关键点 Unity VideoPlayer 播放结束事件Unity AudioSource 播放检测 Unity音视频播放监听器封装笔记&#xff1a;VideoPlayer AudioSource事件触发与编辑器扩展 在 Unity 的多媒体开发中&#xff0c;我们经常需要监听 VideoPlayer 或 AudioSource 的播放状态&#xff0c;以便…

WPF常用技巧汇总

主要用于记录工作中发现的一些问题和常见的解决方法。 此文会持续更新。 >abp new Evan.MyWpfApp -t wpf --old --framework .net8 1. 解决不同屏幕分辨率下的锯齿问题 UseLayoutRounding"True" <Grid UseLayoutRounding"True"><Border Mar…

分数线降低,25西电马克思主义学院(考研录取情况)

1、马克思主义学院各个方向 2、马克思主义学院近三年复试分数线对比 学长、学姐分析 由表可看出&#xff1a; 1、马克思主义理论25年相较于24年下降10分&#xff0c;为355分 3、25vs24推免/统招人数对比 学长、学姐分析 由表可看出&#xff1a; 1、 马克思主义学院25年共接…

【Linux网络】构建UDP服务器与字典翻译系统

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…

【项目管理】成本类计算 笔记

项目管理-相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 &#xff08;一&#xff09;知识总览 项目管理知识域 知识点&#xff1a; &#xff08;项目管理概论、立项管理、十大知识域、配置与变更管理、绩效域&#xff09; 对应&…

div(HTML标准元素)和view(微信小程序专用组件)的主要区别体

div&#xff08;HTML标准元素&#xff09;和view&#xff08;微信小程序专用组件&#xff09;的主要区别体现在以下方面&#xff1a; 一、应用场景与开发框架 ‌适用平台不同‌ div是HTML/CSS开发中通用的块级元素&#xff0c;用于Web页面布局‌&#xff1b;view是微信小程序专…

【C++软件实战问题排查经验分享】UI界面卡顿 | CPU占用高 | GDI对象泄漏 | 线程堵塞 系列问题排查总结

目录 1、UI界面卡顿问题排查 2、软件CPU占用高问题排查 3、UI界面显示异常&#xff08;GDI对象泄漏导致窗口绘制异常&#xff09;问题排查 4、软件线程堵塞&#xff08;包含线程死锁&#xff09;问题排查 5、最后 C软件异常排查从入门到精通系列教程&#xff08;核心精品专…

管理杂谈——采石矶大捷的传奇与启示

南宋抗金史上&#xff0c;岳飞与岳家军的铁血传奇家喻户晓&#xff0c;但另一位力挽狂澜的“文官战神”却常被忽视——他从未掌兵&#xff0c;却在南宋存亡之际整合溃军&#xff0c;以少胜多&#xff0c;缔造采石矶大捷。此人正是虞允文。一介书生何以扭转乾坤&#xff1f;他的…

动态规划-零钱兑换

332.零钱兑换 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。你可以认为每种硬币的数量是无…

SpringAI+DeepSeek大模型应用开发——4 对话机器人

目录​​​​​​​ ​​​​​​​​​​​​​​项目初始化 pom文件 配置模型 ChatClient 同步调用 流式调用 日志功能 对接前端 解决跨域 会话记忆功能 ChatMemory 添加会话记忆功能 会话历史 管理会话id 保存会话id 查询会话历史 完善会话记忆 定义可序列…

Java 关键字

本章列出了Java 语言的所有关键字和“类关键字的单词”。 “受限关键字”是指&#xff0c;它们旨在模块声明中是关键字&#xff0c;在其他情况下则是标识符。 “受限标识符”是指&#xff0c;除非用在某些特定位置&#xff0c;否则他们只是标识符。例如&#xff0c;var一般都…

AI重塑网络安全:机遇与威胁并存的“双刃剑”时代

一、引言 人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;正在深刻改变网络安全行业的格局。从ChatGPT生成钓鱼邮件到AI驱动的漏洞挖掘&#xff0c;从零信任架构的普及到安全大模型的实战应用&#xff0c;AI既是攻击者的“新武器”&#xff0c;也是防御者的“新…

网络原理——UDP

1、 与TCP的关键区别 特性UDPTCP连接方式无连接面向连接可靠性不可靠可靠数据顺序不保证顺序保证顺序传输速度更快相对较慢头部开销8字节20-60字节流量控制无有拥塞控制无有适用场景实时应用、广播/多播可靠性要求高的应用 2、UDP 报文结构 报文结构大致可以分为首部和载荷&a…