有限自动机到正规文法转换器v1.0
1 项目简介
这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典算法精心打造,能够高效且准确地处理用户输入的任意有限自动机,并将其转换为与之等价的正规文法。这一转换器不仅为用户提供了便捷的工具,还为理解和研究自动机与正规文法之间的关系提供了有力的支持,是进行相关学习和研究的得力助手。
2 功能特点
🖥️ “专业GUI界面”: 多选项卡设计,类似专业分析器生成器
🔄 “智能转换”: 自动将FA转换为等价的正规文法
✅ “完整验证”: 输入验证、FA完整性检查
📊 “可视化展示”: FA结构图、转换表、语言特征分析
💾 “文件操作“: 支持保存/加载FA定义、导出结果
🎯 ”示例库“: 内置多个经典FA示例
⚡ ”一键打包“: 支持打包为独立exe文件
🌐 ”跨平台“: 支持Windows、Linux、macOS
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext, filedialog
import json
from collections import defaultdict
import os
import sysclass FAToGrammarConverter:def __init__(self, root):self.root = rootself.root.title("有限自动机到正规文法转换器 v1.0")self.root.geometry("1200x800")self.root.minsize(1000, 700)# 设置图标(如果存在)try:self.root.iconbitmap('icon.ico')except:pass# 存储FA信息self.states = set()self.alphabet = set()self.transitions = defaultdict(list)self.start_state = ""self.final_states = set()self.create_gui()self.create_menu()def create_menu(self):"""创建菜单栏"""menubar = tk.Menu(self.root)self.root.config(menu=menubar)# 文件菜单file_menu = tk.Menu(menubar, tearoff=0)menubar.add_cascade(label="文件", menu=file_menu)file_menu.add_command(label="新建", command=self.new_file, accelerator="Ctrl+N")file_menu.add_command(label="打开", command=self.open_file, accelerator="Ctrl+O")file_menu.add_command(label="保存", command=self.save_file, accelerator="Ctrl+S")file_menu.add_separator()file_menu.add_command(label="导出结果", command=self.export_result)file_menu.add_separator()file_menu.add_command(label="退出", command=self.root.quit)# 编辑菜单edit_menu = tk.Menu(menubar, tearoff=0)menubar.add_cascade(label="编辑", menu=edit_menu)edit_menu.add_command(label="清空输入", command=self.clear_input)edit_menu.add_command(label="加载示例", command=self.load_example)# 工具菜单tools_menu = tk.Menu(menubar, tearoff=0)menubar.add_cascade(label="工具", menu=tools_menu)tools_menu.add_command(label="验证FA", command=self.validate_fa)tools_menu.add_command(label="转换为正规文法", command=self.convert_fa_to_grammar)# 帮助菜单help_menu = tk.Menu(menubar, tearoff=0)menubar.add_cascade(label="帮助", menu=help_menu)help_menu.add_command(label="使用说明", command=self.show_help)help_menu.add_command(label="关于", command=self.show_about)# 绑定快捷键self.root.bind_all("<Control-n>", lambda e: self.new_file())self.root.bind_all("<Control-o>", lambda e: self.open_file())self.root.bind_all("<Control-s>", lambda e: self.save_file())def create_gui(self):# 创建主框架main_frame = ttk.Frame(self.root, padding="10")main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))# 配置网格权重self.root.columnconfigure(0, weight=1)self.root.rowconfigure(0, weight=1)main_frame.columnconfigure(0, weight=1)main_frame.rowconfigure(1, weight=1)# 创建Notebook(选项卡)notebook = ttk.Notebook(main_frame)notebook.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))# 输入选项卡input_frame = ttk.Frame(notebook, padding="10")notebook.add(input_frame, text="输入有限自动机")# 结果选项卡result_frame = ttk.Frame(notebook, padding="10")notebook.add(result_frame, text="转换结果")# 可视化选项卡visual_frame = ttk.Frame(notebook, padding="10")notebook.add(visual_frame, text="可视化")self.create_input_tab(input_frame)self.create_result_tab(result_frame)self.create_visual_tab(visual_frame)# 状态栏self.status_bar = ttk.Label(main_frame, text="就绪", relief=tk.SUNKEN, anchor=tk.W)self.status_bar.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(5, 0))def create_input_tab(self, parent):"""创建输入选项卡"""# 标题title_label = ttk.Label(parent, text="有限自动机输入界面", font=("Microsoft YaHei", 16, "bold"))title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))# 左侧输入区域left_frame = ttk.LabelFrame(parent, text="FA定义", padding="10")left_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 10))left_frame.columnconfigure(1, weight=1)# 状态集合ttk.Label(left_frame, text="状态集合 Q:", font=("Microsoft YaHei", 10, "bold")).grid(row=0, column=0, sticky=tk.W, pady=5)self.states_entry = ttk.Entry(left_frame, width=40, font=("Consolas", 10))self.states_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=5)ttk.Label(left_frame, text="用逗号分隔,如: q0,q1,q2", font=("Microsoft YaHei", 9), foreground="gray").grid(row=0, column=2, sticky=tk.W, padx=(10, 0))# 字母表ttk.Label(left_frame, text="字母表 Σ:", font=("Microsoft YaHei", 10, "bold")).grid(row=1, column=0, sticky=tk.W, pady=5)self.alphabet_entry = ttk.Entry(left_frame, width=40, font=("Consolas", 10))self.alphabet_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=5)ttk.Label(left_frame, text="用逗号分隔,如: a,b,c", font=("Microsoft YaHei", 9), foreground="gray").grid(row=1, column=2, sticky=tk.W, padx=(10, 0))# 转换函数ttk.Label(left_frame, text="转换函数 δ:", font=("Microsoft YaHei", 10, "bold")).grid(row=2, column=0, sticky=tk.W, pady=5)self.transitions_text = scrolledtext.ScrolledText(left_frame, height=8, width=40, font=("Consolas", 10))self.transitions_text.grid(row=2, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=5)ttk.Label(left_frame, text="每行一个转换\n格式: 起始状态,输入符号,目标状态", font=("Microsoft YaHei", 9), foreground="gray").grid(row=2, column=2, sticky=tk.W, padx=(10, 0))# 初始状态ttk.Label(left_frame, text="初始状态 q₀:", font=("Microsoft YaHei", 10, "bold")).grid(row=3, column=0, sticky=tk.W, pady=5)self.start_state_entry = ttk.Entry(left_frame, width=40, font=("Consolas", 10))self.start_state_entry.grid(row=3, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=5)# 终结状态ttk.Label(left_frame, text="终结状态 F:", font=("Microsoft YaHei", 10, "bold")).grid(row=4, column=0, sticky=tk.W, pady=5)self.final_states_entry = ttk.Entry(left_frame, width=40, font=("Consolas", 10))self.final_states_entry.grid(row=4, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=5)ttk.Label(left_frame, text="用逗号分隔", font=("Microsoft YaHei", 9), foreground="gray").grid(row=4, column=2, sticky=tk.W, padx=(10, 0))# 右侧按钮区域right_frame = ttk.LabelFrame(parent, text="操作", padding="10")right_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(10, 0))# 按钮ttk.Button(right_frame, text="加载示例", command=self.load_example, width=15).grid(row=0, column=0, pady=5, sticky=tk.W+tk.E)ttk.Button(right_frame, text="验证FA", command=self.validate_fa, width=15).grid(row=1, column=0, pady=5, sticky=tk.W+tk.E)ttk.Button(right_frame, text="转换为正规文法", command=self.convert_fa_to_grammar, style="Accent.TButton", width=15).grid(row=2, column=0, pady=10, sticky=tk.W+tk.E)ttk.Button(right_frame, text="清空输入", command=self.clear_input, width=15).grid(row=3, column=0, pady=5, sticky=tk.W+tk.E)# 示例列表ttk.Label(right_frame, text="示例库:", font=("Microsoft YaHei", 10, "bold")).grid(row=4, column=0, sticky=tk.W, pady=(20, 5))self.example_listbox = tk.Listbox(right_frame, height=6, font=("Microsoft YaHei", 9))self.example_listbox.grid(row=5, column=0, sticky=(tk.W, tk.E), pady=5)self.example_listbox.insert(0, "识别以'a'开头的字符串")self.example_listbox.insert(1, "识别偶数个'a'")self.example_listbox.insert(2, "识别包含'ab'的字符串")self.example_listbox.bind("<Double-Button-1>", self.load_selected_example)# 配置权重parent.columnconfigure(0, weight=2)parent.columnconfigure(1, weight=1)parent.rowconfigure(1, weight=1)def create_result_tab(self, parent):"""创建结果选项卡"""# 结果显示区域result_frame = ttk.LabelFrame(parent, text="转换结果 - 正规文法", padding="10")result_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))result_frame.columnconfigure(0, weight=1)result_frame.rowconfigure(0, weight=1)self.result_text = scrolledtext.ScrolledText(result_frame, height=25, width=100, font=("Consolas", 11))self.result_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))# 按钮区域button_frame = ttk.Frame(parent)button_frame.grid(row=1, column=0, pady=(10, 0))ttk.Button(button_frame, text="复制结果", command=self.copy_result).grid(row=0, column=0, padx=5)ttk.Button(button_frame, text="保存结果", command=self.save_result).grid(row=0, column=1, padx=5)ttk.Button(button_frame, text="清空结果", command=self.clear_result).grid(row=0, column=2, padx=5)parent.columnconfigure(0, weight=1)parent.rowconfigure(0, weight=1)def create_visual_tab(self, parent):"""创建可视化选项卡"""# 可视化区域visual_frame = ttk.LabelFrame(parent, text="FA结构可视化", padding="10")visual_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))visual_frame.columnconfigure(0, weight=1)visual_frame.rowconfigure(0, weight=1)self.visual_text = scrolledtext.ScrolledText(visual_frame, height=25, width=100, font=("Consolas", 10))self.visual_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))parent.columnconfigure(0, weight=1)parent.rowconfigure(0, weight=1)def load_selected_example(self, event):"""加载选中的示例"""selection = self.example_listbox.curselection()if selection:index = selection[0]if index == 0:self.load_example()elif index == 1:self.load_even_a_example()elif index == 2:self.load_contains_ab_example()def load_example(self):"""加载示例FA - 识别以'a'开头的字符串"""self.clear_input()self.states_entry.insert(0, "q0,q1,q2")self.alphabet_entry.insert(0, "a,b")self.transitions_text.insert(1.0, "q0,a,q1\nq1,a,q1\nq1,b,q1\nq0,b,q2\nq2,a,q2\nq2,b,q2")self.start_state_entry.insert(0, "q0")self.final_states_entry.insert(0, "q1")self.update_status("已加载示例:识别以'a'开头的字符串")def load_even_a_example(self):"""加载示例FA - 识别偶数个'a'"""self.clear_input()self.states_entry.insert(0, "q0,q1")self.alphabet_entry.insert(0, "a,b")self.transitions_text.insert(1.0, "q0,a,q1\nq1,a,q0\nq0,b,q0\nq1,b,q1")self.start_state_entry.insert(0, "q0")self.final_states_entry.insert(0, "q0")self.update_status("已加载示例:识别偶数个'a'")def load_contains_ab_example(self):"""加载示例FA - 识别包含'ab'的字符串"""self.clear_input()self.states_entry.insert(0, "q0,q1,q2")self.alphabet_entry.insert(0, "a,b")self.transitions_text.insert(1.0, "q0,a,q1\nq0,b,q0\nq1,a,q1\nq1,b,q2\nq2,a,q2\nq2,b,q2")self.start_state_entry.insert(0, "q0")self.final_states_entry.insert(0, "q2")self.update_status("已加载示例:识别包含'ab'的字符串")def clear_input(self):"""清空所有输入"""self.states_entry.delete(0, tk.END)self.alphabet_entry.delete(0, tk.END)self.transitions_text.delete(1.0, tk.END)self.start_state_entry.delete(0, tk.END)self.final_states_entry.delete(0, tk.END)self.update_status("已清空输入")def update_status(self, message):"""更新状态栏"""self.status_bar.config(text=message)def parse_input(self):"""解析用户输入的FA"""try:# 解析状态集合states_str = self.states_entry.get().strip()if not states_str:raise ValueError("状态集合不能为空")self.states = set(s.strip() for s in states_str.split(','))# 解析字母表alphabet_str = self.alphabet_entry.get().strip()if not alphabet_str:raise ValueError("字母表不能为空")self.alphabet = set(s.strip() for s in alphabet_str.split(','))# 解析转换函数transitions_str = self.transitions_text.get(1.0, tk.END).strip()if not transitions_str:raise ValueError("转换函数不能为空")self.transitions = defaultdict(list)for line in transitions_str.split('\n'):line = line.strip()if line:parts = [p.strip() for p in line.split(',')]if len(parts) != 3:raise ValueError(f"转换函数格式错误: {line}")from_state, symbol, to_state = partsif from_state not in self.states:raise ValueError(f"状态 {from_state} 不在状态集合中")if to_state not in self.states:raise ValueError(f"状态 {to_state} 不在状态集合中")if symbol not in self.alphabet:raise ValueError(f"符号 {symbol} 不在字母表中")self.transitions[(from_state, symbol)].append(to_state)# 解析初始状态self.start_state = self.start_state_entry.get().strip()if not self.start_state:raise ValueError("初始状态不能为空")if self.start_state not in self.states:raise ValueError("初始状态不在状态集合中")# 解析终结状态final_states_str = self.final_states_entry.get().strip()if not final_states_str:raise ValueError("终结状态不能为空")self.final_states = set(s.strip() for s in final_states_str.split(','))for state in self.final_states:if state not in self.states:raise ValueError(f"终结状态 {state} 不在状态集合中")return Trueexcept ValueError as e:messagebox.showerror("输入错误", str(e))self.update_status(f"错误: {str(e)}")return Falsedef validate_fa(self):"""验证FA的完整性和正确性"""if not self.parse_input():return Falsetry:# 检查是否所有状态都可达reachable_states = set()stack = [self.start_state]reachable_states.add(self.start_state)while stack:current = stack.pop()for symbol in self.alphabet:if (current, symbol) in self.transitions:for next_state in self.transitions[(current, symbol)]:if next_state not in reachable_states:reachable_states.add(next_state)stack.append(next_state)unreachable = self.states - reachable_states# 显示验证结果result = ["FA验证结果:", "=" * 30]result.append(f"状态总数: {len(self.states)}")result.append(f"字母表大小: {len(self.alphabet)}")result.append(f"转换函数数量: {sum(len(v) for v in self.transitions.values())}")result.append(f"可达状态: {len(reachable_states)}")if unreachable:result.append(f"不可达状态: {', '.join(unreachable)}")result.append("警告: 存在不可达状态!")else:result.append("✓ 所有状态都可达")# 检查FA类型is_dfa = all(len(transitions) <= 1 for transitions in self.transitions.values())result.append(f"FA类型: {'DFA (确定有限自动机)' if is_dfa else 'NFA (非确定有限自动机)'}")messagebox.showinfo("验证结果", "\n".join(result))self.update_status("FA验证完成")return Trueexcept Exception as e:messagebox.showerror("验证错误", f"验证过程中发生错误: {str(e)}")self.update_status(f"验证失败: {str(e)}")return Falsedef convert_fa_to_grammar(self):"""将FA转换为正规文法"""if not self.parse_input():returntry:# 构造正规文法grammar = self.fa_to_regular_grammar()self.display_grammar(grammar)self.display_visualization()self.update_status("转换完成")except Exception as e:messagebox.showerror("转换错误", f"转换过程中发生错误: {str(e)}")self.update_status(f"转换失败: {str(e)}")def fa_to_regular_grammar(self):"""FA到正规文法的转换算法"""productions = []# 对于每个转换 δ(q, a) = p,创建产生式 q → apfor (from_state, symbol), to_states in self.transitions.items():for to_state in to_states:production = f"{from_state} → {symbol}{to_state}"productions.append(production)# 对于每个终结状态 f ∈ F,创建产生式 f → εfor final_state in self.final_states:production = f"{final_state} → ε"productions.append(production)grammar = {'non_terminals': self.states,'terminals': self.alphabet,'start_symbol': self.start_state,'productions': productions}return grammardef display_grammar(self, grammar):"""显示转换后的正规文法"""result = []result.append("╔" + "═" * 60 + "╗")result.append("║" + "转换结果:正规文法 G = (N, T, P, S)".center(60) + "║")result.append("╚" + "═" * 60 + "╝")result.append("")# 非终结符集合result.append("【非终结符集合】")result.append(f"N = {{{', '.join(sorted(grammar['non_terminals']))}}}")result.append("")# 终结符集合result.append("【终结符集合】")result.append(f"T = {{{', '.join(sorted(grammar['terminals']))}}}")result.append("")# 开始符号result.append("【开始符号】")result.append(f"S = {grammar['start_symbol']}")result.append("")# 产生式集合result.append("【产生式集合】")result.append("P = {")for i, production in enumerate(grammar['productions']):prefix = " " if i < len(grammar['productions']) - 1 else " "suffix = "," if i < len(grammar['productions']) - 1 else ""result.append(f"{prefix}{production}{suffix}")result.append("}")result.append("")# 转换算法说明result.append("╔" + "═" * 60 + "╗")result.append("║" + "转换算法说明".center(60) + "║")result.append("╚" + "═" * 60 + "╝")result.append("")result.append("算法步骤:")result.append("1. 对于FA中的每个转换 δ(q, a) = p,")result.append(" 创建产生式 q → ap")result.append("")result.append("2. 对于FA中的每个终结状态 f ∈ F,")result.append(" 创建产生式 f → ε")result.append("")result.append("3. FA的初始状态成为文法的开始符号")result.append("")result.append("✓ 此正规文法生成的语言与原FA识别的语言相同")result.append("✓ 转换保持了语言的等价性")# 显示结果self.result_text.delete(1.0, tk.END)self.result_text.insert(1.0, '\n'.join(result))def display_visualization(self):"""显示FA的可视化表示"""visual = []visual.append("╔" + "═" * 60 + "╗")visual.append("║" + "有限自动机结构可视化".center(60) + "║")visual.append("╚" + "═" * 60 + "╝")visual.append("")# 状态图表示visual.append("【状态转换图】")visual.append("")# 绘制状态for state in sorted(self.states):state_type = ""if state == self.start_state:state_type += "[初始]"if state in self.final_states:state_type += "[终结]"visual.append(f"状态 {state} {state_type}")# 显示从该状态出发的转换for symbol in sorted(self.alphabet):if (state, symbol) in self.transitions:for target in self.transitions[(state, symbol)]:visual.append(f" --{symbol}--> {target}")visual.append("")# 转换表visual.append("【转换表】")visual.append("δ" + " " * 8 + "|" + " ".join(f"{s:>8}" for s in sorted(self.alphabet)))visual.append("-" * (10 + 9 * len(self.alphabet)))for state in sorted(self.states):row = f"{state:<8} |"for symbol in sorted(self.alphabet):if (state, symbol) in self.transitions:targets = ",".join(self.transitions[(state, symbol)])row += f"{targets:>8} "else:row += f"{'∅':>8} "visual.append(row)visual.append("")visual.append("【语言描述】")visual.append("此FA识别的正规语言L(FA)的特征:")# 简单的语言特征分析if len(self.final_states) == 1 and self.start_state in self.final_states:visual.append("- 包含空字符串ε")# 检查是否存在循环has_loops = any(state in self.transitions.get((state, symbol), []) for state in self.states for symbol in self.alphabet)if has_loops:visual.append("- 可以生成无限长度的字符串")visual.append(f"- 字母表: {{{', '.join(sorted(self.alphabet))}}}")visual.append(f"- 状态数: {len(self.states)}")self.visual_text.delete(1.0, tk.END)self.visual_text.insert(1.0, '\n'.join(visual))def copy_result(self):"""复制结果到剪贴板"""self.root.clipboard_clear()self.root.clipboard_append(self.result_text.get(1.0, tk.END))self.update_status("结果已复制到剪贴板")def save_result(self):"""保存结果到文件"""filename = filedialog.asksaveasfilename(defaultextension=".txt",filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")])if filename:try:with open(filename, 'w', encoding='utf-8') as f:f.write(self.result_text.get(1.0, tk.END))self.update_status(f"结果已保存到: {filename}")except Exception as e:messagebox.showerror("保存错误", f"无法保存文件: {str(e)}")def clear_result(self):"""清空结果"""self.result_text.delete(1.0, tk.END)self.visual_text.delete(1.0, tk.END)self.update_status("结果已清空")def new_file(self):"""新建文件"""self.clear_input()self.clear_result()self.update_status("新建文件")def open_file(self):"""打开文件"""filename = filedialog.askopenfilename(filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")])if filename:try:with open(filename, 'r', encoding='utf-8') as f:data = json.load(f)self.clear_input()self.states_entry.insert(0, ','.join(data.get('states', [])))self.alphabet_entry.insert(0, ','.join(data.get('alphabet', [])))self.start_state_entry.insert(0, data.get('start_state', ''))self.final_states_entry.insert(0, ','.join(data.get('final_states', [])))transitions_text = '\n'.join([f"{t[0]},{t[1]},{t[2]}" for t in data.get('transitions', [])])self.transitions_text.insert(1.0, transitions_text)self.update_status(f"已打开文件: {filename}")except Exception as e:messagebox.showerror("打开错误", f"无法打开文件: {str(e)}")def save_file(self):"""保存文件"""if not self.parse_input():returnfilename = filedialog.asksaveasfilename(defaultextension=".json",filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")])if filename:try:transitions = []for (from_state, symbol), to_states in self.transitions.items():for to_state in to_states:transitions.append([from_state, symbol, to_state])data = {'states': list(self.states),'alphabet': list(self.alphabet),'transitions': transitions,'start_state': self.start_state,'final_states': list(self.final_states)}with open(filename, 'w', encoding='utf-8') as f:json.dump(data, f, ensure_ascii=False, indent=2)self.update_status(f"已保存到: {filename}")except Exception as e:messagebox.showerror("保存错误", f"无法保存文件: {str(e)}")def export_result(self):"""导出转换结果"""if not self.result_text.get(1.0, tk.END).strip():messagebox.showwarning("导出警告", "没有可导出的结果")returnfilename = filedialog.asksaveasfilename(defaultextension=".txt",filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")])if filename:try:with open(filename, 'w', encoding='utf-8') as f:f.write(self.result_text.get(1.0, tk.END))f.write("\n" + "="*60 + "\n")f.write("可视化信息:\n")f.write(self.visual_text.get(1.0, tk.END))self.update_status(f"结果已导出到: {filename}")except Exception as e:messagebox.showerror("导出错误", f"无法导出文件: {str(e)}")def show_help(self):"""显示帮助信息"""help_text = """
有限自动机到正规文法转换器 - 使用说明1. 输入有限自动机:• 状态集合:输入所有状态,用逗号分隔• 字母表:输入所有输入符号,用逗号分隔• 转换函数:每行一个转换,格式为"起始状态,输入符号,目标状态"• 初始状态:输入初始状态• 终结状态:输入所有终结状态,用逗号分隔2. 操作步骤:• 点击"加载示例"可以快速加载预设的FA• 点击"验证FA"检查输入的FA是否正确• 点击"转换为正规文法"执行转换• 在"转换结果"选项卡查看结果• 在"可视化"选项卡查看FA结构3. 文件操作:• 支持保存/打开FA定义文件(JSON格式)• 支持导出转换结果(文本格式)4. 快捷键:• Ctrl+N:新建• Ctrl+O:打开• Ctrl+S:保存5. 注意事项:• 状态名和符号不能包含逗号• 支持确定和非确定有限自动机• 转换后的正规文法与原FA等价"""messagebox.showinfo("使用说明", help_text)def show_about(self):"""显示关于信息"""about_text = """
有限自动机到正规文法转换器 v1.0"""messagebox.showinfo("关于", about_text)def main():root = tk.Tk()# 设置样式style = ttk.Style()style.theme_use('clam')# 配置颜色主题style.configure('Accent.TButton', background='#0078D4', foreground='white')app = FAToGrammarConverter(root)# 设置窗口居中root.update_idletasks()x = (root.winfo_screenwidth() // 2) - (root.winfo_width() // 2)y = (root.winfo_screenheight() // 2) - (root.winfo_height() // 2)root.geometry(f"+{x}+{y}")root.mainloop()if __name__ == "__main__":main()
安装依赖
pip install -r requirements.txt
# 有限自动机到正规文法转换器依赖
# Python 3.6+ 兼容
# GUI框架 (通常随Python安装)
# tkinter - 内置模块
# 打包工具
pyinstaller>=5.0.0
# 其他依赖
setuptools>=40.0.0
```bash
# 运行构建脚本
build.bat
# 或手动构建
pip install pyinstaller
pyinstaller --onefile --windowed --name="FA转换器" fa_to_grammar_converter.py
# 运行exe文件
dist\FA转换器.exe
# 设置执行权限
chmod +x build.sh
# 运行构建脚本
./build.sh
# 运行可执行文件
./dist/FA转换器
相关文章:

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...