编译原理实验2——自上而下语法分析LL1(包含去消除左递归、消除回溯)

文章目录

    • 实验目的
    • 实现流程
      • 代码
      • 运行结果
        • 测试1(含公共因子)
        • 测试2(经典的i+i*i文法,且含左递归)
        • 测试3(识别部分标识符)
    • 总结

实验目的

实现自上而下分析的LL1语法分析器,给出分析过程

实现流程

代码

代码逻辑

1.预处理

  • 去除多余空格:如“ S - > aB”,处理成“S-> aB”
  • 拆解候选式:对于某一产生式,若有多个候选式,根据 | 符号拆解为多个产生式。
  • 获取开始符号:默认输入的第一个非终结符为开始符
  • 消除左递归和回溯(公共因子)
  • 获取非终结符和终极符
    2.计算first集和follow集
    3.检查是否符合LL1文法
    4.建立预测分析表
    5.对输入串进行LL1分析
import copy
from collections import defaultdict
import pandas as pdclass LL1:def __init__(self, input_str_list):self.input_str_list = input_str_listself.formulas_dict = {}  # 存储产生式 ---dict<set> 形式self.S = ""  # 开始符self.Vt = []  # 终结符self.Vn = []  # 非终结符self.first = defaultdict(set)  # 初始化First集合self.follow = defaultdict(set)  # 初始化Follow集合self.table = {}  # 预测分析表self.info = {}# 消除直接左递归def eliminate_direct_left_recursion(self, grammar, non_terminal):productions = grammar[non_terminal]recursive_productions = []alphabet_list = [chr(i) for i in range(ord('A'), ord('Z') + 1)]  # A-Z,用于给新非终结符命名for production in productions:  # 找到含有左递归的候选式if production.startswith(non_terminal):recursive_productions.append(production)if len(recursive_productions) > 0:# 命名为A-Z且不与原有存在的非终结符重名for ch in alphabet_list:if ch not in grammar.keys():new_non_terminal = chbreak# S = Sab \ Scd \ T \ F# 更新原始非终结符的产生式  S = (T\F) S'grammar[non_terminal] = [p + new_non_terminal for p in productions if not p.startswith(non_terminal)]# 添加新的非终结符的产生式  S'=(ab\cd) S'grammar[new_non_terminal] = [p[1:] + new_non_terminal for p in recursive_productions ifp.startswith(non_terminal)]grammar[new_non_terminal].append('@')  # S'=(ab\cd)S' \ @return grammar# 往后预测,看是否会出现间接左递归def is_recruse(self, grammar, non_terminals, iidx, cur, pre):# print(f"=====cur:{cur}, pre:{pre}=====")check = Falseset_front_con = set()  # pre右侧所有可能递归的vnfor pre_production in grammar[pre]:if pre_production[0].isupper():set_front_con.add(pre_production[0])# print("pre_set:", set_front_con)set_back_con = set()for i in range(iidx, len(non_terminals)):  # 遍历所有非终结符 curback = cur......最后一个终结符cur_back = non_terminals[i]# print("cur_back", cur_back)if i == len(non_terminals) - 1:  # 若为最后一个终结符,则加入自身set_back_con.add(cur_back)for cur_back_pro in grammar[cur_back]:  # 遍历当前cur_back的候选式if cur_back_pro.startswith(cur):set_back_con.add(cur_back)# print("cur_set:", set_back_con)if len(set_front_con & set_back_con) != 0:  # 有交集check = Truereturn check# 消除左递归(先间接后直接)def eliminate_left_recursion(self, grammar):non_terminals = list(grammar.keys())[::-1]  # 逆序,将开始符放到最后replaced_vn = []  # 记录被替换代入掉的非终结符for i in range(len(non_terminals)):  # 遍历所有非终结符cur = non_terminals[i]# 间接左递归--》直接左递归for j in range(i):  # 遍历 pre1,pre2,pre3.....cur的非终结符(cur前面的终结符)pre = non_terminals[j]new_productions = []for cur_production in grammar[cur]:if cur_production.startswith(pre):  # 在cur的所有候选式中,找到以pre开头的候选式if self.is_recruse(grammar, non_terminals, i, cur, pre):  # 若最终能产生间接左递归,进行代入合并处理rest_str = cur_production.replace(pre, '', 1)  # 截取cur的该候选式去除首字符后的剩余字符replaced_vn.append(pre)for pre_production in grammar[pre]:  # 加入到pre的所有候选式后面if pre_production + rest_str not in new_productions:new_productions.append(pre_production + rest_str)else:  # 不进行代入合并处理if cur_production not in new_productions:new_productions.append(cur_production)else:if cur_production not in new_productions:new_productions.append(cur_production)grammar[cur] = new_productionsgrammar = self.eliminate_direct_left_recursion(grammar, cur)  # 消除当前的直接左递归# 消除冗余产生式(那些被替换代入的产生式)for vn in replaced_vn:del grammar[vn]return grammar# 消除回溯def eliminate_huisu(self, grammar):alphabet_list = [chr(i) for i in range(ord('A'), ord('Z') + 1)]  # A-Z,用于给新非终结符命名while True:grammar_copy = grammar.copy()for left, right in grammar_copy.items():right = list(right)prefixes = []# 找所有项目的公共因子for i in range(len(right)):for j in range(i + 1, len(right)):str1, str2 = right[i], right[j]index = 0while index < min(len(str1), len(str2)) and str1[index] == str2[index]:index += 1if index >= 1:have = Falsefor pre in prefixes:if pre[0] == str1[0]:have = Trueif not have:if str1[:index] not in prefixes:prefixes.append(str1[:index])# =================================================================if len(prefixes) == 0:continuetmp_match = defaultdict(set)tmp_not_match = set()# for pre in prefixes:#     for r_candidate in right:#         if r_candidate.startswith(pre):#             tmp_match[pre].add(r_candidate)for r_candidate in right:match=Falsefor pre in prefixes:if r_candidate.startswith(pre):tmp_match[pre].add(r_candidate)match=Truebreakif not match:tmp_not_match.add(r_candidate)new_ini_pro = set()for vn, right in tmp_match.items():new_r_pro = []new_vn = ""for r_candidate in right:for ch in alphabet_list:  # 根据alphabet_list给new_vn命名if ch not in grammar.keys():new_vn = chbreakif r_candidate[len(vn):] == "":  # 切片后为空(即只剩一个字符),则新产生式补@if "@" not in new_r_pro:new_r_pro.append('@')else:if r_candidate[len(vn):] not in new_r_pro:new_r_pro.append(r_candidate[len(vn):])grammar[new_vn] = new_r_pronew_ini_pro.add(vn + new_vn)grammar[left] = list(new_ini_pro.union(tmp_not_match))# print(grammar)if grammar_copy == grammar:  # 不再发生改变,则退出whilebreakreturn grammar# 预处理def step1_pre_process(self, grammar_list):formulas_dict = {}  # 存储产生式 ---dict<set> 形式S = " "  # 开始符Vt = []  # 终结符Vn = []  # 非终结符for production in grammar_list:left, right = production.split('->')if "|" in right:r_list = right.split("|")formulas_dict[left] = []for r in r_list:if r not in formulas_dict[left]:formulas_dict[left].append(r)else:if left in formulas_dict.keys():formulas_dict[left].append(right)else:formulas_dict[left] = [right]  # 若left不存在,会自动创建 left: 空set# 文法开始符S = list(formulas_dict.keys())[0]# 消除左递归和回溯formulas_dict = self.eliminate_left_recursion(formulas_dict)formulas_dict = self.eliminate_huisu(formulas_dict)print("=========消除左递归和回溯后的产生式=========")for left,right in formulas_dict.items():print(left+"->"+"".join(right))print("=========基本信息=========")# 获取终结符和非终结符for left, right in formulas_dict.items():if left not in Vn:Vn.append(left)for r_candidate in right:for symbol in r_candidate:if not symbol.isupper() and symbol != '@':if symbol not in Vt:Vt.append(symbol)# 打印非终结符和终结符print("开始符:", S)print("非终结符:", Vn)print("终结符:", Vt)return formulas_dict, Vn, Vt, Sdef cal_symbol_first(self, symbol):# 如果是终结符,直接加入到First集合if not symbol.isupper():self.first[symbol].add(symbol)else:for r_candidate in self.formulas_dict[symbol]:i = 0while i < len(r_candidate):next_symbol = r_candidate[i]# 如果是非终结符,递归计算其First集合if next_symbol.isupper():self.cal_symbol_first(next_symbol)self.first[symbol] = self.first[symbol].union(self.first[next_symbol] - {'@'})  # 合并first(next_symbol)/{@}if '@' not in self.first[next_symbol]:break# 如果是终结符,加入到First集合else:self.first[symbol].add(next_symbol)breaki += 1# 如果所有符号的First集合都包含ε,将ε加入到First集合if i == len(r_candidate):self.first[symbol].add('@')# 计算First集合def step2_cal_first(self, formulas_dict):# 计算所有非终结符的First集合for vn in formulas_dict.keys():self.cal_symbol_first(vn)# 计算所有终结符的First集合for vt in self.Vt:self.cal_symbol_first(vt)# 计算ε的First集self.cal_symbol_first('@')# 打印First集合for key, value in self.first.items():print(f"First({key}): {value}")# 计算Follow集合1——考虑 添加first(Vn后一个非终结符)/{ε}, 而 不考虑 添加follow(left)def cal_follow1(self, vn):self.follow[vn] = set()if vn == self.S:  # 若为开始符,加入#self.follow[vn].add('#')for left, right in self.formulas_dict.items():  # 遍历所有文法,取出左部单Vn、右部候选式集合for r_candidate in right:  # 遍历当前 右部候选式集合i = 0while i <= len(r_candidate) - 1:  # 遍历当前 右部候选式if r_candidate[i] == vn:  # ch == Vnif i + 1 == len(r_candidate):  # 如果是最后一个字符  >>>>>  S->....Vself.follow[vn].add('#')breakelse:  # 后面还有字符  >>>>> S->...V..while i != len(r_candidate):i += 1if r_candidate[i] == vn:  # 又遇到Vn,回退 >>>>> S->...V..V..i -= 1breakif r_candidate[i].isupper():  # 非终结符  >>>>> S->...VA..self.follow[vn] = self.follow[vn].union(self.first[r_candidate[i]] - {'@'})if '@' in self.first[r_candidate[i]]:  # 能推空  >>>>> S->...VA..  A可推空if i + 1 == len(r_candidate):  # 是最后一个字符  >>>>> S->...VA  A可推空 可等价为 S->...Vself.follow[vn].add('#')breakelse:  # 不能推空 >>>>> S->...VA..  A不可推空breakelse:  # 终结符  >>>>> S->...Va..self.follow[vn].add(r_candidate[i])breakelse:i += 1# 计算Follow集合2——考虑 添加follow(left)def cal_follow2(self, vn):for left, right in self.formulas_dict.items():  # 遍历所有文法,取出左部单Vn、右部候选式集合for r_candidate in right:  # 遍历当前 右部候选式集合i = 0while i <= len(r_candidate) - 1:  # 遍历当前 右部候选式if r_candidate[i] == vn:  # 找到Vnif i == len(r_candidate) - 1:  # 如果当前是最后一个字符,添加 follow(left) >>>>>  S->..Vself.follow[vn] = self.follow[vn].union(self.follow[left])breakelse:  # 看看后面的字符能否推空 >>>>>  S->..V..while i != len(r_candidate):i += 1if '@' in self.first[r_candidate[i]]:  # 能推空  >>>>> S->..VB..  B可推空if i == len(r_candidate) - 1:  # 且是最后一个字符  >>>>> S->..VB  B可推空self.follow[vn] = self.follow[vn].union(self.follow[left])breakelse:  # 不是最后一个字符,继续看  >>>>> S->..VBA..  B可推空continueelse:  # 不能推空  >>>>>  S->..VB..  B不可为空breaki += 1# 计算所有Follow集合的总长度,用于判断是否还需要继续完善def cal_follow_total_Len(self):total_Len = 0for vn, vn_follow in self.follow.items():total_Len += len(vn_follow)return total_Lendef step3_cal_follow(self, formulas_dict):# 先用 cal_follow1 算for vn in formulas_dict.keys():self.cal_follow1(vn)# 在循环用 cal_follow2 算, 直到所有follow集总长度不再变化,说明计算完毕while True:old_len = self.cal_follow_total_Len()for vn in formulas_dict.keys():self.cal_follow2(vn)new_len = self.cal_follow_total_Len()if old_len == new_len:break# 打印Follow集合for key, value in self.follow.items():print(f"Follow({key}): {value}")# 检测是否符合LL(1)文法def step4_check_LL1(self, formulas_dict, first, follow):# 检查每个产生式右部,多个候选式中每个候选首字符的first集是否相交(回溯)for left, right in formulas_dict.items():if len(right) >= 2:#           print(f"{left}: {right}")s = set()for r_candidate in right:old_len = len(s)s = s.union(first[r_candidate[0]])new_len = len(s)if old_len == new_len:return False# 每个产生式A,若饿ε∈first(A),则first(A) ∩ follow(A) = 空集for left, right in formulas_dict.items():if '@' in first[left]:if first[left] & follow[left]:  # 有交集return Falsereturn True# 建立LL(1)预测分析表def step5_create_table(self, formulas_dict, first, follow):tab_dict = {}for left, right in formulas_dict.items(): # 对于每一个产生式,求出其每个候选式的first集for r_candidate in right:idx=0cur_can_first = set()while True:if r_candidate[idx].isupper():cur_can_first = cur_can_first.union(first[r_candidate[idx]] - {'@'})else:cur_can_first.add(r_candidate[idx])idx += 1if idx >= len(r_candidate) or ('@' not in first[r_candidate[idx-1]]):breakfor fi in cur_can_first:if fi == '@':for fo in follow[left]:tab_dict[(left, fo)] = '@'else:tab_dict[(left, fi)]=r_candidatedf = pd.DataFrame(list(tab_dict.items()), columns=['Key', 'Value'])df['Vn'] = [x[0] for x in df['Key']]df['Vt'] = [x[1] for x in df['Key']]tab_df = df.pivot(index='Vn', columns='Vt', values='Value')print(tab_df)return tab_dict, tab_df#  LL1分析def step6_LL1_analyse(self, s, S, Vn, Vt, table):s = list(s)  # 将字符串转为list类型,方便增删s.append('#')  # 末尾加入#sp = 0  # 字符串指针stack = []  # 栈stack.append('#')  # 进#stack.append(S)  # 进开始符msg = ""  # 分析情况step = 0  # 步骤数info_step, info_stack, info_str, info_msg, info_res = [], [], [], [], ""while sp != len(s):ch = s[sp]  # 获取当前输入字符top = stack[-1]  # 获取栈顶元素step += 1info_step.append(step)info_stack.append(''.join(stack))info_str.append(''.join(s[sp:]))info_msg.append(msg)if top in Vt:  # 栈顶元素是  终结符if top == ch:top = stack.pop()  # 栈顶出栈sp += 1  # str指针后移一位msg = f"'{ch}'匹配"else:info_res = f"error: 栈顶元素{top} 与 字符{ch} 不匹配!"breakelif top in Vn:  # 栈顶元素是 非终结符if (top, ch) in table.keys():  # table中含有该项top = stack.pop()  # 先出栈stack.extend(reversed(table[(top, ch)]))  # 逆序入栈msg = f"{top}->" + table[(top, ch)]else:# tk_show_info += f"error: table找不到匹配的({top},{ch})\n"info_res = f"error: table找不到匹配的({top},{ch})"breakelif top == '#':  # 栈顶元素是 文法结束符if ch == '#':# tk_show_info += f"Success!\n"info_res = f"Success!"breakelse:# tk_show_info += f"error: 栈顶元素{top} 与 字符{ch} 不匹配!\n"info_res = f"error: 栈顶元素{top} 与 字符{ch} 不匹配!"breakelif top == '@':  # 栈顶元素是 εtop = stack.pop()  # 直接出栈εmsg = f"'@'出栈"continueinfo = {"info_step": info_step,"info_stack": info_stack,"info_str": info_str,"info_msg": info_msg,"info_res": info_res}return infodef init(self):self.formulas_dict, self.Vn, self.Vt, self.S = self.step1_pre_process(self.input_str_list)print("=========First、Follow=========")self.step2_cal_first(self.formulas_dict)self.step3_cal_follow(self.formulas_dict)check_res = self.step4_check_LL1(self.formulas_dict, self.first, self.follow)# =========判断是否合法=========if check_res:print("\n经过分析,该文法 符合 LL(1)文法\n")else:print("\n经过分析,该文法 不符合 LL(1)文法\n")returnprint("=========预测分析表=========")self.table, df_tab = self.step5_create_table(self.formulas_dict, self.first, self.follow)def solve(self,s):self.info = self.step6_LL1_analyse(s,self.S,self.Vn,self.Vt,self.table)print("=========分析过程=========")for i in range(len(self.info["info_step"])):print("{:<15}  {:<15}  {:<15}  {:<15}".format(str(self.info["info_step"][i]), self.info["info_stack"][i],self.info["info_str"][i], self.info["info_msg"][i]))return self.infoif __name__ == "__main__":grammar1 = [ # abb、abcbcbcbb等等"E->abA|aB|abB|cd|cf","A->cbA|b","B->e"]grammar2 = [ # i+i*i、(i+i)*i等等"E->E+T|T","T->T*F|F","F->(E)|i"]grammar3=[ # 部分标识符文法: 形如aa、a1、aaa、aa1"E->LL|LD|LLL|LLD","L->a|b|c","D->0|1|2|3|4|5|6|7|8|9"]grammar4=[ # aad、bd、cbd、aacbd等等"S->AaS|BbS|d","A->a","B->@|c"]ll1 = LL1(grammar3)ll1.init()analyse_str="ab1"ll1.solve(analyse_str)

运行结果

测试1(含公共因子)

输入:
文法:在这里插入图片描述
分析串:在这里插入图片描述

输出:
在这里插入图片描述
在这里插入图片描述

测试2(经典的i+i*i文法,且含左递归)

输入:
文法:在这里插入图片描述
分析串:在这里插入图片描述

输出:
在这里插入图片描述
在这里插入图片描述

测试3(识别部分标识符)

输入:
文法:在这里插入图片描述
分析串:在这里插入图片描述

输出:
在这里插入图片描述
在这里插入图片描述

总结

实现过程中,对于消除左递归、消除回溯、first集、follow集的实现查阅了很多资料,修改了很多次代码,目前来说暂时能适用很多文法了。

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

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

相关文章

[C#] 如何使用ScottPlot.WPF在WPF桌面程序中绘制图表

什么是ScottPlot.WPF&#xff1f; ScottPlot.WPF 是一个开源的数据可视化库&#xff0c;用于在 WPF 应用程序中创建高品质的绘图和图表。它是基于 ScottPlot 库的 WPF 版本&#xff0c;提供了简单易用的 API&#xff0c;使开发人员能够通过简单的代码创建各种类型的图表&#…

2、6作业

TCP和UDP的区别 TCP和UDP都是通信协议 TCP提供有连接的&#xff0c;稳定的&#xff0c;无误码无失真无乱序无丢失的通信 UDP提供无连接的&#xff0c;尽力的&#xff0c;可能误码可能乱序&#xff0c;可能丢失的通信 TCP每发一个数据包就需要对方回应一个应答包&#xff0c…

自定义npm包从vue2升级到vue3遇到的问题解决

1.执行npm run build时报错&#xff1a; (node:16724) UnhandledPromiseRejectionWarning: SyntaxError: Unexpected token ‘??’ at Loader.moduleStrategy (internal/modules/esm/translators.js:149:18 解决&#xff1a;更新node版本 查看了我使用的node版本是14.21.3&…

《合成孔径雷达成像算法与实现》Figure6.10

clc clear close all参数设置 距离向参数设置 R_eta_c 20e3; % 景中心斜距 Tr 2.5e-6; % 发射脉冲时宽 Kr 20e12; % 距离向调频率 alpha_os_r 1.2; % 距离过采样率 Nrg 320; % 距离线采样数 距离向…

UUID和雪花(Snowflake)算法该如何选择?

UUID和雪花(Snowflake)算法该如何选择&#xff1f; UUID 和 Snowflake 都可以生成唯一标识&#xff0c;在分布式系统中可以说是必备利器&#xff0c;那么我们该如何对不同的场景进行不同算法的选择呢&#xff0c;UUID 简单无序十分适合生成 requestID&#xff0c; Snowflake 里…

Flink实战六_直播礼物统计

接上文&#xff1a;Flink实战五_状态机制 1、需求背景 现在网络直播平台非常火爆&#xff0c;在斗鱼这样的网络直播间&#xff0c;经常可以看到这样的总榜排名&#xff0c;体现了主播的人气值。 人气值计算规则&#xff1a;用户发送1条弹幕互动&#xff0c;赠送1个荧光棒免费…

07-使用Package、Crates、Modules管理项目

上一篇&#xff1a;06-枚举和模式匹配 当你编写大型程序时&#xff0c;组织代码将变得越来越重要。通过对相关功能进行分组并将具有不同功能的代码分开&#xff0c;您可以明确在哪里可以找到实现特定功能的代码&#xff0c;以及在哪里可以改变功能的工作方式。 到目前为止&…

必收藏!第六版CCF推荐会议C类国际学术会议!(中国计算机学会)

中国计算机学会 中国计算机学会&#xff08;CCF&#xff09;是全国性、学术性、非营利的学术团体&#xff0c;由从事计算机及相关科学技术领域的个人和单位自愿组成。作为独立社团法人&#xff0c;CCF是中国科学技术协会的成员之一&#xff0c;是全国一级学会&#xff01; CCF的…

JavaScript基础第四天

JavaScript 基础第四天 今天我们学习js的函数&#xff0c;包括普通函数、匿名函数、箭头函数以及函数作用域。 1. 函数的初体验 1.1. 什么是函数 函数是 JavaScript 中的基本组件之一。一个函数是 JavaScript 过程一组执行任务或计算值的语句。要使用一个函数&#xff0c;你…

Linux下库函数、静态库与动态库

库函数 什么是库 库是二进制文件, 是源代码文件的另一种表现形式, 是加了密的源代码; 是一些功能相近或者是相似的函数的集合体. 使用库有什么好处 提高代码的可重用性, 而且还可以提高程序的健壮性;可以减少开发者的代码开发量, 缩短开发周期. 库制作完成后, 如何给用户…

大模型为什么会有 tokens 限制?

人是以字数来计算文本长度&#xff0c;大语言模型 &#xff08;LLM&#xff09;是以 token 数来计算长度的。LLM 使用 token 把一个句子分解成若干部分。 token 可以是一个单词、一个单词中的一个部分、甚至是一个字符&#xff0c;具体取决于它使用的标记化方法 (tokenization…

为电子表格嵌入数据库,Excel/WPS一键升级为管理系统

将Excel表格转化为管理系统&#xff0c;这款工具能够实现只需导入表格数据&#xff0c;即可自动生成相应的软件和APP。 表格办公的烦恼&#xff0c;有遇到吧&#xff1f; 对于具有一定规模的企业而言&#xff0c;各类表格如同繁星般众多&#xff0c;既有日常使用的常规表格&a…

泰克示波器——TBS2000系列界面整体介绍

目录 1.1 通道区域面板标识1.2 示波器测试输出&#xff08;检测探针与设置的好坏&#xff09;1.3 面板其他快捷按钮1.4 波器整体界面 1.1 通道区域面板标识 在通道面板的下方标识有示波器的通道属性以及参数值&#xff0c;如我使用的型号为“TBS2104X”的示波器&#xff0c;面…

格子表单GRID-FORM | 文档网站搭建(VitePress)与部署(Github Pages)

格子表单/GRID-FORM已在Github 开源&#xff0c;如能帮到您麻烦给个星&#x1f91d; GRID-FORM 系列文章 基于 VUE3 可视化低代码表单设计器嵌套表单与自定义脚本交互文档网站搭建&#xff08;VitePress&#xff09;与部署&#xff08;Github Pages&#xff09; 效果预览 格…

如何使用VMware分享出来的虚拟机系统(OVF文件)

前言 这几天看到很多小伙伴都在安装虚拟机&#xff0c;但成不成就不知道了。 所以小白准备把自己安装完成的系统打包分享给小伙伴。 如果你需要已经安装完成的虚拟系统&#xff0c;可以获取哦&#xff01;打开即用&#xff01; 虚拟机系统包括&#xff1a; Win10 专业版 Wi…

anaconda+pytorch+pycharm安装总结

1.下载最新的Anaconda,目前是python3.11适用 anaconda官网 安装教程 卸载并重新安装的教程 &#xff08;如果找不到火绒清理注册表垃圾的位置可以拉到文章底部查看&#xff09; 2.pytorch安装&#xff0c;注意python版本、cuda版本和pytorch版本的适配 安装教程 3.pycharm安装和…

C++: 模板初阶

目录 引子&#xff1a; 函数模板 格式&#xff1a; 例子&#xff1a; 函数模板的实例化&#xff1a; 隐形实例化&#xff1a;让编译器根据实参推演模板参数的实际类型 显式实例化&#xff1a; 在函数名后的<>中指定模板参数的实际类型 模板参数的匹配原则 类模…

ssm+vue的校园一卡通密钥管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的校园一卡通密钥管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

C++入门学习(二十五)do-while循环

do { // 代码块&#xff0c;至少会执行一次 } while (条件); 对比一下while和do-while循环&#xff1a; 因为while循环先判断条件&#xff0c;所以数字10直接就没有进入for循环里&#xff0c;卡在了判断条件这一步&#xff0c;所以就没有输出数据&#xff1b; do-while循环是…

clickhouse计算前后两点间经纬度距离

问题 计算如图所示前后两点经纬度的距离&#xff1f; 方法 1、用开窗函数将如图所示数据下移一行 selectlongitude lon1,latitude lat1,min(longitude) over(order by time1 asc rows between 1 PRECEDING and 1 PRECEDING) lon2,min(latitude) over(order by time1 asc row…