当前位置: 首页 > article >正文

用仓颉语言搞定编译原理实验:从正则表达式到DFA的保姆级实现(附完整代码)

用仓颉语言实现编译原理实验从正则表达式到DFA的实战指南第一次接触编译原理实验时看着那些晦涩的算法描述和数学符号我完全不知道如何下手。直到用仓颉语言完整实现了从正则表达式到NFA再到DFA的转换过程才真正理解了这些概念背后的运作机制。本文将分享如何用这个新兴语言一步步构建编译原理中的核心算法避开我踩过的那些坑。1. 环境准备与基础知识在开始编码前我们需要明确几个关键概念。正则表达式Regular Expression是一种描述字符串模式的工具而有限自动机Finite Automaton则是识别这些模式的机器。NFA非确定有限自动机和DFA确定有限自动机是两种不同的自动机模型。仓颉语言虽然语法简洁但在处理这类算法时有其独特优势类型系统强类型检查能在编码阶段发现许多潜在错误函数式特性高阶函数和模式匹配适合处理语法树性能优化编译为原生代码适合处理大量状态转换安装仓颉开发环境很简单# 在Linux/macOS上安装仓颉 curl -sSL https://install.cangjie-lang.org | sh # 验证安装 cangjie --version提示建议使用最新版本的仓颉目前是0.8.2早期版本可能缺少某些标准库功能。2. 正则表达式到NFA的转换Thompson构造法是实现这一转换的标准算法。它的核心思想是将正则表达式分解为基本组成部分然后递归组合这些部分的NFA。2.1 正则表达式的预处理首先需要将中缀形式的正则表达式转换为后缀表达式逆波兰表示法这样更容易用栈来处理。以下是关键步骤处理连接操作在相邻字符间插入显式的连接符如将ab变为ab处理运算符优先级* 连接 |使用Shunting-yard算法转换为后缀形式// 添加连接符号的函数实现 func addConcatSymbols(regex: String): String { var result let chars regex.chars() for i in 0..chars.size-1 { let c1 chars[i] let c2 chars[i1] result c1 // 在需要的地方插入连接符 if !isOperator(c1) !isOperator(c2) c2 ! ) c1 ! ( { result } } result chars.last return result }2.2 NFA的数据结构表示在仓颉中我们可以用以下结构表示NFA状态struct NFAState { // 转移表索引0为ε转移1-26对应字母a-z var transitions Array(27, {_ ArrayListInt()}) } var nfaStates ArrayListNFAState() var stateCounter 0这种设计考虑了ε转移索引0字母转移索引1-26对应a-z动态扩展的状态集合2.3 实现Thompson构造法Thompson构造法包含几种基本操作基本字符创建两个状态通过该字符连接连接将两个NFA首尾相连选择|创建新的开始和结束状态并联两个NFA闭包*添加ε转移形成循环// 处理闭包操作的实现 func handleClosure(): Void { let end stack.pop() let start stack.pop() let newStart allocateState() let newEnd allocateState() // 添加ε转移 nfaStates[newStart].transitions[0].add(start) nfaStates[newStart].transitions[0].add(newEnd) nfaStates[end].transitions[0].add(start) nfaStates[end].transitions[0].add(newEnd) stack.push(newStart) stack.push(newEnd) }注意仓颉中没有现成的栈结构需要自己用ArrayList模拟记得在操作前后检查边界条件。3. NFA到DFA的转换子集构造法是实现这一转换的标准算法核心是处理ε闭包和状态集合的映射。3.1 ε闭包的计算ε闭包是指从某状态出发仅通过ε转移能到达的所有状态的集合。// 计算单个状态的ε闭包 func epsilonClosure(state: Int): HashSetInt { var closure HashSetInt() var stack ArrayListInt() stack.add(state) while !stack.isEmpty() { let current stack.removeLast() closure.add(current) for nextState in nfaStates[current].transitions[0] { if !closure.contains(nextState) { stack.add(nextState) } } } return closure }3.2 子集构造算法实现算法步骤如下计算NFA初始状态的ε闭包作为DFA的第一个状态对每个DFA状态和输入符号计算转移后的状态集合如果该集合是新状态加入DFA状态集重复直到所有状态都被处理// 子集构造法主函数 func subsetConstruction(): Void { // 初始化DFA状态 let startClosure epsilonClosure(nfaStartState) dfaStates.add(startClosure) var unprocessed 0 while unprocessed dfaStates.size() { let currentDFAState dfaStates[unprocessed] // 处理每个输入符号a-z for symbol in 1..27 { var newState HashSetInt() // 计算move(T,a) for nfaState in currentDFAState { newState.addAll(nfaStates[nfaState].transitions[symbol]) } // 计算ε闭包 var symbolClosure HashSetInt() for state in newState { symbolClosure.addAll(epsilonClosure(state)) } if !symbolClosure.isEmpty() { // 查找或添加新的DFA状态 match dfaStates.indexOf(symbolClosure) { case Some(index) dfaTransitions[unprocessed][symbol-1] index case None dfaStates.add(symbolClosure) dfaTransitions[unprocessed][symbol-1] dfaStates.size()-1 } } } unprocessed } }3.3 DFA的优化表示与NFA不同DFA的转移是确定性的可以用更紧凑的结构表示struct DFAState { var isAccepting: Bool var transitions Array(26, {_ OptionInt()}) // a-z的转移 } var dfa ArrayListDFAState()4. 测试与调试技巧实现算法后需要验证其正确性。我推荐以下测试策略4.1 单元测试样例选择有代表性的正则表达式进行测试简单字符a连接ab选择a|b闭包a*组合(a|b)*abbfunc testRegex(regex: String, testCases: Array(String, Bool)): Void { println(测试正则表达式: ${regex}) buildNFA(regex) convertToDFA() for (input, expected) in testCases { let result simulateDFA(input) println(输入${input}: 预期${expected}, 实际${result}) assert(result expected) } }4.2 常见问题排查在实现过程中我遇到了这些问题及解决方案状态混淆为每个状态添加唯一ID打印时显示详细信息ε循环在ε闭包计算中添加已访问标记防止无限循环边界条件特别处理空字符串和空正则表达式的情况性能问题对大型正则表达式添加状态数限制和超时检测4.3 可视化调试技巧虽然仓颉没有图形库但可以用文本方式可视化自动机func printDFA(): Void { println(DFA状态数: ${dfa.size()}) for (i, state) in dfa.enumerate() { print(状态${i}: ) if state.isAccepting { print([接受] ) } for (symbol, target) in state.transitions.enumerate() { match target { case Some(t) print(${Char(symbol97)}-${t} ) case None () } } println() } }5. 仓颉语言特性带来的挑战作为一门新兴语言仓颉在实现这类算法时有一些特别之处需要注意5.1 缺少标准数据结构仓颉的标准库相对精简需要自己实现一些常用数据结构// 简易栈实现 struct StackT { private var elements ArrayListT() func push(_ item: T): Void { elements.add(item) } func pop(): T { if elements.isEmpty() { panic(栈下溢) } return elements.removeLast() } func top(): T { return elements.last() } }5.2 类型系统的严格性仓颉的类型检查非常严格这在带来安全性的同时也需要更多类型声明// 处理Option类型的模式匹配 func getTransition(state: Int, symbol: Char): OptionInt { let index symbol.toInt() - a.toInt() if index 0 index 26 { return dfa[state].transitions[index] } return None }5.3 性能考量当处理复杂正则表达式时算法性能变得重要。以下是几个优化点使用HashSet而不是ArrayList存储状态集合对ε闭包计算结果进行缓存提前终止不可能匹配的输入字符串对DFA状态进行最小化虽然不在本文讨论范围// 带缓存的ε闭包计算 var epsilonCache HashMapInt, HashSetInt() func cachedEpsilonClosure(state: Int): HashSetInt { match epsilonCache.get(state) { case Some(cached) return cached case None let closure computeEpsilonClosure(state) epsilonCache.put(state, closure) return closure } }实现完整个项目后我对编译原理中的这些基础概念有了更直观的理解。仓颉语言虽然小众但其简洁的语法和强大的类型系统让算法实现过程变得清晰可控。最让我惊喜的是通过这个实验我不仅学会了如何实现正则表达式引擎还掌握了将一个复杂问题分解为可管理模块的思维方式。

相关文章:

用仓颉语言搞定编译原理实验:从正则表达式到DFA的保姆级实现(附完整代码)

用仓颉语言实现编译原理实验:从正则表达式到DFA的实战指南 第一次接触编译原理实验时,看着那些晦涩的算法描述和数学符号,我完全不知道如何下手。直到用仓颉语言完整实现了从正则表达式到NFA再到DFA的转换过程,才真正理解了这些概…...

悟空率先接入国产最强编程模型Qwen3.6-Plus

4月2日,阿里巴巴正式发布新一代大语言模型Qwen3.6-Plus,阿里在企业级市场的旗舰AI应用悟空率先完成接入。Qwen3.6-Plus在代码、智能体、推理、原生多模态等能力上整体性能大幅增强,在智能体编程SWE-bench系列评测、真实世界智能体任务Claw-Ev…...

别让SDF警告淹没你!芯片后仿真中那些‘不起眼’却至关重要的VCS编译选项详解

别让SDF警告淹没你!芯片后仿真中那些‘不起眼’却至关重要的VCS编译选项详解 当数字IC设计进入后仿真阶段,工程师们常常会陷入海量警告信息的泥潭。特别是当SDF(Standard Delay Format)文件反标时产生的各类警告,往往…...

五大赛道齐亮相!第四届世界科学智能大赛启动报名,首设人文科学赛道

随着人工智能深入科研实践,它不仅在各领域课题的预测、计算等方面屡创新高,也正介入曾被认为高度依赖人类直觉与经验的文化阐释工作。继第四届世界科学智能大赛的创新赛道“AI4S智能体CNS挑战赛”在一个月前率先发布,吹响了自主科研智能体的攻…...

绿色软件制作:TranslucentTB便携版开发全攻略

绿色软件制作:TranslucentTB便携版开发全攻略 【免费下载链接】TranslucentTB A lightweight utility that makes the Windows taskbar translucent/transparent. 项目地址: https://gitcode.com/gh_mirrors/tr/TranslucentTB 在Windows个性化定制领域&#…...

WarcraftHelper技术适配方案:让经典RTS游戏重获现代硬件支持

WarcraftHelper技术适配方案:让经典RTS游戏重获现代硬件支持 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 痛点解析:魔兽争霸…...

基于DRAMsim3的扩散模型训练加速仿真:内存时延与能耗分析

基于DRAMsim3的扩散模型训练加速仿真:内存时延与能耗分析 摘要 扩散模型在生成式AI领域取得了巨大成功,但其训练过程极其昂贵,主要体现在对内存带宽的巨大需求(尤其是Attention机制和梯度存储)。本文聚焦于利用DRAMsim3模拟器,在系统架构层面仿真扩散模型(如DDPM)训练…...

告别B站缓存格式困扰:m4s-converter让视频文件处理效率提升80%

告别B站缓存格式困扰:m4s-converter让视频文件处理效率提升80% 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 一、痛点直击&#xf…...

如何在Windows 11上高效配置三指拖拽功能:完整实用指南

如何在Windows 11上高效配置三指拖拽功能:完整实用指南 【免费下载链接】ThreeFingersDragOnWindows Enables macOS-style three-finger dragging functionality on Windows Precision touchpads. 项目地址: https://gitcode.com/gh_mirrors/th/ThreeFingersDragO…...

别再只用L2损失了!手把手教你用PyTorch实现MS-SSIM+L1混合损失,图像修复效果大提升

超越L1/L2:用MS-SSIM混合损失打造专业级图像修复模型 当你在深夜调试一个图像超分辨率模型时,屏幕上的结果让你皱起了眉头——那些应该清晰锐利的边缘却像被水浸湿的水彩画一样模糊不清,而平坦的天空区域则布满了令人不快的颗粒状伪影。这可能…...

打造个人离线书库:番茄小说下载器全场景应用指南

打造个人离线书库:番茄小说下载器全场景应用指南 【免费下载链接】Tomato-Novel-Downloader 番茄小说下载器不精简版 项目地址: https://gitcode.com/gh_mirrors/to/Tomato-Novel-Downloader 番茄小说下载器是一款开源工具,专为小说爱好者设计&am…...

Windows DLL注入工具Xenos全攻略:从原理到实践的系统指南

Windows DLL注入工具Xenos全攻略:从原理到实践的系统指南 【免费下载链接】Xenos Windows dll injector 项目地址: https://gitcode.com/gh_mirrors/xe/Xenos 一、技术原理:Xenos注入引擎的底层架构 1.1 三级注入引擎的工作机制 Xenos作为专业的…...

Linux下objdump反汇编实战:从二进制文件到可读代码的深度解析

1. 初识objdump:二进制世界的翻译官 第一次接触objdump时,我把它比作"二进制世界的翻译官"。这个比喻来自我调试段错误时的经历——当时面对崩溃的core dump文件手足无措,直到同事教我用了objdump -d。这个GNU工具链中的瑞士军刀&a…...

从网球场到棋盘:深入对比Moravec与Forstner算子在真实影像中的表现差异与选型建议

从网球场到棋盘:深入对比Moravec与Forstner算子在真实影像中的表现差异与选型建议 当我们需要从一张照片中找出那些独特的"地标"时——无论是网球场的边角线还是棋盘上的交叉点——特征点提取算法就像一位经验丰富的侦探,用不同的策略标记出关…...

通信萌新们注意了!今天咱们玩点刺激的——用MATLAB手搓各种QAM调制的性能对比。准备好你的小本本,咱们边写代码边分析,包教包会

基于4QAM,16QAM,64QAM调制方式下经过AWGN信道的性能分析 均包含加噪声前后的星座图、误码率和误符号率性能对比,该程序一共10张仿真图,可学习性非常强先上硬货,看看怎么生成4QAM的星座图。掏出这段代码: M …...

KEIL MDK实战:3分钟将常用C文件封装成LIB库(附标准库管理技巧)

KEIL MDK高效工程管理:C文件封装LIB库的进阶实践 在嵌入式开发领域,随着项目规模扩大,工程文件管理往往成为影响开发效率的关键瓶颈。特别是对于STM32开发者而言,标准外设库、常用算法模块等重复使用的代码如何高效管理&#xff0…...

[LaTeX] 使用minipage与subfigure实现高效多图排版(附代码型图片处理技巧)

1. 为什么需要minipage和subfigure? 写论文或者技术文档时,经常遇到需要把多张图片并排展示的情况。比如对比实验效果图、不同角度的产品展示、代码片段对比等。传统做法是每张图单独插入,但这样会导致图片间距不一致、对齐困难,最…...

别再死记硬背了!用FFmpeg实战拆解H.264码流,手把手教你读懂NALU头

从字节到画面:FFmpeg实战解析H.264码流中的NALU奥秘 当你用手机观看一段高清视频时,每秒25帧的画面流畅切换背后,是H.264编码算法在默默工作。但你是否好奇过,这些压缩后的数据究竟如何组织?今天我们将用FFmpeg这把&qu…...

Vue3 + xterm.js 4.x + WebSocket 打造现代化Web终端实战指南

1. 为什么选择Vue3 xterm.js 4.x WebSocket组合? 在构建现代化Web终端时,技术选型直接影响开发效率和最终用户体验。Vue3提供了响应式编程范式和组件化开发优势,xterm.js 4.x是最新版本的浏览器终端模拟器,而WebSocket则实现了…...

别再用requests硬刚了!用Selenium+Playwright搞定小红书评论爬虫(附完整Cookie处理方案)

突破小红书反爬:Selenium与Playwright实战对比与Cookie处理全指南 在小红书这类社交电商平台的数据挖掘中,评论爬取一直是开发者面临的棘手挑战。传统requests库直接调用API的方式看似简单,但面对小红书日益完善的反爬机制——包括动态Cookie…...

深度解析 Claude Code v2.1.88 源码:技术栈与底层实现全揭秘(基于流出架构资料)

深度解析 Claude Code v2.1.88 源码:技术栈与底层实现全揭秘(基于流出架构资料) 摘要:2026年3月31日,Claude Code v2.1.88 相关技术资料(含TypeScript工程架构、核心模块实现逻辑,合计51.2万行代码量级)公开流出,包含其核心架构、工具系统、安全机制等全部实现细节。…...

从“制造”到“智造”:TVA如何成为智能工厂的底层代码?

当我们在谈论AI视觉检测,尤其是AI智能体视觉检测(TVA)时,我们究竟在谈论什么?如果只把它看作是“替代几个质检工人”的工具,那就太低估它的价值了。在产业升级的洪流中,每一次技术的迭代&#x…...

STM32C8T6+AS608指纹模块实战:从接线到代码调试的全流程避坑指南

STM32C8T6AS608指纹模块实战:从接线到代码调试的全流程避坑指南 指纹识别技术正逐渐渗透到日常生活的各个角落,从手机解锁到门禁系统,这项技术为我们提供了便捷与安全的双重保障。对于嵌入式开发者而言,将指纹识别功能整合到自己的…...

告别“卡脖子”:TVA的0.8秒背后柔性生产与极致效率

作为生产厂长,每天最头疼的不是做出好产品,而是如何在“多品种、小批量、快交期”的频繁切线中,保证产线不停机、不降速。现代汽车零部件企业的生产节奏越来越快,冲压产线往往要求几秒钟甚至零点几秒就出一个件。在这种极限节拍下…...

AI Memory 全景解析:让 Agent 真正“记住”你

AI Memory 全景解析:让 Agent 真正"记住"你 你有没有遇到过这种场景:明明昨天告诉 AI 助手你喜欢简洁的代码风格,今天它又开始写冗长的注释;或者你费心纠正了一个错误,下次对话它照犯不误。这就是 AI 没有记…...

Windows 10/11下Frida逆向分析环境搭建避坑指南(含ADB驱动安装)

Windows 10/11逆向工程实战:Frida环境搭建全流程与疑难解析 逆向工程的世界就像一场数字考古,而Frida无疑是当前最趁手的工具之一。但很多新手在Windows平台搭建Frida环境时,往往会陷入Python版本地狱、ADB驱动失效、设备连接失败等连环陷阱。…...

别再只盯着Protobuf了!从DDS到Thrift,聊聊不同IDL在自动驾驶和机器人项目里的真实选型

自动驾驶与机器人系统中的IDL选型实战:从DDS到Thrift的深度解析 在自动驾驶和机器人系统的开发中,接口定义语言(IDL)的选择往往决定了整个通信架构的成败。当激光雷达每秒产生数十万点云数据,当多个传感器需要在毫秒级完成数据融合&#xff…...

Fedora 40 虚拟机避坑指南:VMware 17.5 安装与内核降级实战(解决卡顿与兼容性问题)

Fedora 40 虚拟机性能优化全攻略:从内核调优到图形加速的深度实践 当你在VMware Workstation 17.5上运行Fedora 40时,是否遇到过系统卡顿、响应迟缓的问题?这并非个例——最新Linux发行版与虚拟化平台间的兼容性挑战,往往让开发者…...

像素剧本圣殿保姆级教程:从零配置到输出标准格式剧本的5步详解

像素剧本圣殿保姆级教程:从零配置到输出标准格式剧本的5步详解 1. 认识像素剧本圣殿 像素剧本圣殿是一款专为剧本创作者设计的AI辅助工具,它基于强大的Qwen2.5-14B-Instruct模型进行深度优化,特别适合需要快速生成专业格式剧本的创作者。与…...

【微知】Mellanox网卡配置异常?mlxconfig reset全解与实战场景指南

1. Mellanox网卡配置异常?先别慌 遇到Mellanox网卡配置异常时,很多工程师第一反应是重装驱动或者更换硬件。其实在大多数情况下,用对mlxconfig reset这个神器就能快速解决问题。我处理过上百台配备Mellanox网卡的服务器,发现80%的…...