技海无涯:正则表达式相关的知识和技术(3)——编程技巧:堆栈的本质探讨

编程技巧——堆栈的本质探讨

如果我要说本章的编程技巧就是为了介绍堆栈的使用技巧,你可能会笑掉大牙:哈哈,堆栈,这不是小儿科吗?!!

是的,每个编程的人都知道的堆栈,而且说起堆栈,大家肯定会马上想到“后进先出(LIFO)”,这是教科书关于堆栈本质的解释。

没错,堆栈的本质是LIFO,但绝不是简单的先进后出就完了,结合各种各样的压栈出栈操作,堆栈可以实现很强大的功能。下面就以正则表达式涉及的两个例子来说明堆栈的妙用。

1) 通过堆栈来完成正则表达式->NFA.

在介绍表达式的时候提到过我们书写的正则表达式是中缀表达式,但计算机识别的时候是需要将中缀表达式转换成NFA的。

问题出现了:正则表达式是有优先级的,例如*号优先级最高,然后()里面是当作一个完整的单元来处理的。例如如下正则表达式:

ab*|c(d|a)

按照Thompson算法,正确的处理顺序应该如下:

(1)生成a的NFA

(2)生成b*的NFA,将a和b*的NFA连接起来,假设NFA为X;

(3)生成c的NFA

(4)遇到括号,生成括号里面的NFA;

(5)将c的NFA和括号的NFA连接起来;假设NFA为Y;

(6)将NFA X和NFA Y连接起来,得到最终的NFA,假设为Z。

人是可以识别出这种先后顺序,但是计算机扫描的时候是从左往右扫描,没有超前扫描的功能,例如计算机扫描到b的时候,并不知道后面马上会有一个*,那计算机该怎么处理呢?

这正是堆栈发挥作用的时候。通过堆栈的压栈和出栈操作,我们可以改变操作顺序,以让计算机来完成类似于人的分析过程。

这里还用到一个技巧就是双堆栈,即:运算符一个堆栈,操作数一个堆栈,下面我们看看用堆栈,计算机如何完成正则表达式到NFA的转换。

(1)将a压入操作数栈;

(2)读到b的时候,由于正则表达式本身没有连接操作符,因此将b压入操作数栈的同时,我们需要同时自己压一个&符号到运算符栈,方便后面处理。

(3)读入*,由于*号优先级比&高,所以继续将*压栈;

(4)读入|,由于‘|’比*号优先级低,所以这个时候就要把*弹出来,再把b弹出来,生成b*的NFA;

(5)这时运算栈里面只有&,由于‘|’比&优先级也低,所以就要把&弹出来,然后把a弹出来,和第4步已经完成的b*的NFA生成ab*的NFA。

(6)此时运算栈里面已经没有运算符了,所以把‘|’压入。

(………………………………剩下的就请各位自己分析了)

 

2) 通过堆栈来完成NFA->DFA.

将NFA转换到DFA的子集构造算法最核心的就是构造子集闭包,也就是算法中的epsilon_closure函数。

epsilon_closure函数的关键在于找到“经过1个或多个epsilon转换后能够达到的状态集”。我们看一个样例:

 

如果状态转换关系是上面这种形式,那就很简单,直接递归或者循环都可以搞定,但实际的正则表达式的转换关系不可能是这种一维的形式的,而是一个有向无环图,类似于如下这种复杂的形式:

 

 

这种情况无论是递归还是循环都无法解决,而利用堆栈就可以巧妙的解决这个图的遍历问题。此处使用堆栈的原理为:取出栈顶状态,看这个状态经过1个epsilon转换能够到达哪些状态,将这些状态又都压入栈。如下是上面样例的操作步骤:

1、  栈初始为1,

2、  弹出1,发现1经过1个epsilon转换能够达到2、3、4,因此压入2、3、4;

3、  按照第二步操作(后面都一样,不再赘述),弹出4,压入7、8;

4、  弹出8;8没有可以经过epsilon转换能够到达的状态,因此此处没有压栈操作了(后面也一样,不在赘述)。

5、  弹出7;压入9;

6、  弹出9;

(………………………………剩下的就请各位自己分析了)

细心的大侠们可能会发现,上面的处理步骤和人的分析过程是一样的,也就是说通过堆栈让计算机模拟了人的思维。

 

综合以上两个例子,我们可以得出堆栈真正的本质:改变计算机的顺序处理,让计算机能够模拟人的处理步骤。

所以大家在使用堆栈的时候不要死抱着LIFO,然后先全部压栈再全部出栈,这样的操作在实际应用中除了将字符串倒序外没有任何用处,堆栈真正的妙用是“在处理过程中进行压栈出栈”!

========================未完待续==================================

时间: 2024-09-24 08:45:04

技海无涯:正则表达式相关的知识和技术(3)——编程技巧:堆栈的本质探讨的相关文章

技海无涯:正则表达式相关的知识和技术(1)——表达式

正则表达式,看似简单,实则博大精深.简简单单几个字符:|.*.(.)--却能够演绎出无穷无尽的变化.初看正则表达式,其实就是一串子字符串,但隐藏在这字符串背后的各种各样的知识.技能.技巧,却一点也不简单. 以前在学习<编译原理(龙书)>的时候,也是一目十行的将其跳过,这次偶尔需要用到正则表达式,然后自己就上网搜了搜,结果发现水不是一般的深,耗费了3个晚上的时间搜索.查阅,才稍微理清了这些相关知识的关系和脉络,于是稍作整理归纳,既为了加深自己的理解,也为了共享给各位. 在正式开始之前,先将相关东

技海无涯:正则表达式相关的知识和技术(4)——自动机(完结篇)

自动机 自动机,顾名思义,就是能够自动完成事情的"机器".但是为什么要用自动机,什么又叫"自动"呢? 我们看看普通的处理方式,简单的就是if了,例如:判断某字符串是否是"Hello,Word",我们都会这么写: if(str == "Hello,Word") 普通的比较可以满足大部分的场景,但对于正则表达式这种比较的话,普通的if就完全不能满足了,例如a*b|c,可以是如下字符串: ab aab aaaab c ----. 这

技海无涯:正则表达式相关的知识和技术(2)——算法

    转换算法 为了让正则表达式最终能够被机器识别,并且能够用其来匹配目标字符串,必须首先将正则表达式转换为NFA或者DFA(后面介绍)两种等价的自动机,一般的转换过程如下: 正则表达式-①->NFA-②->DFA. 当然也可以直接这样转换,当然这个算法复杂度更高: 正则表达式-③->DFA. 上面的每个过程对应一个算法,下面我们分别简单的介绍三种算法.   ①正则表达式-->NFA:Thompson 算法 算法本身是比较简单的,算法的关键如下:每个字符都构造一个NFA,然后用e

Java正则表达式基础入门知识_正则表达式

众所周知,在程序开发中,难免会遇到需要匹配.查找.替换.判断字符串的情况发生,而这些情况有时又比较复杂,如果用纯编码方式解决,往往会浪费程序员的时间及精力.因此,学习及使用正则表达式,便成了解决这一矛盾的主要手段. 大家都知道,正则表达式是一种可以用于模式匹配和替换的规范,一个正则表达式就是由普通的字符(例如字符a到z)以及特殊字符(元字符)组成的文字模式,它 用以描述在查找文字主体时待匹配的一个或多个字符串.正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配. 一:什么是正则表达式

接触C#的服务器端正则表达式相关验证

初次接触C#的服务器端正则表达式相关验证 后续将会在这个基础上 将这个C#的服务器端正则表达式相关内容 集结在这里 入门示例如下 //验证 string strFileName = "S_123456_200701.zip" System.Text.RegularExpressions.Regex strReg = new System.Text.RegularExpressions.Regex(@"^S_\d{6}_\d{6}\.zip$"); if (strRe

网络-请问谁知道 Van Jacobson TCP/IP包头压缩 相关的知识和具体报文格式?

问题描述 请问谁知道 Van Jacobson TCP/IP包头压缩 相关的知识和具体报文格式? 新人一个,没有什么分:Van Jacobson TCP/IP包头压缩降低了TCP/IP包头的大小到接近3个字节,这对于慢速的串行线路来说是一大提高.IP压缩协议配置选项用于指示接收压缩包的能力.如果需要两个方向都进行压缩则需要双方独立申请. 4.1.PPP协议域值 在传送IP包时PPP协议域被设置为以下值: 0021 类型IP.IP协议不是TCP,或包是一个数据段,或未经过压缩. 002d 压缩的T

javascript正则表达式的基本知识

javascript正则表达式的基本知识 1     javascript 正则对象创建 和用法     声明javascript 正则表达式          var reCat = new RegExp("cat");       你也可以      var reCat = /cat/;      //Perl 风格   (推荐) 2 学习最常用的 test exec match search  replace  split 6个方法    1) test  检查指定的字符串是否存

每个人在自己看病的经历中都能收获到很多相关的知识和经验

所谓"久病成良医",每个人在自己看病的经历中都能收获到很多相关的知识和经验,把这些经验搬到线上来,打造一个病患交流社区,则正是厦门初创公司"病患如我"正在做的事. "病患如我"是一个UGC病患交流社区.同时还具备数据挖掘能力,会收集整理论坛中患者贡献的,与疾病诊疗相关的数据. 在"病患如我"每一个对应疾病的分区里,用户讨论的药品种类.医院类别等数据会被进行统计,并整理成为统计图.而数据获取的方式主要有三个,一是患者在注册的时候

正则表达式的基本知识_正则表达式

正则表达式的基本知识: 元字符: 正则表达式的威力在于其能够在模式中包含选择和循环.它们通过使用 有两组不同的元字符:一种是模式中除了方括号内都能被识别的,还有一种是在方括号内被识别的.方括号之外的元字符有这些:  有数种用途的通用转义符  断言目标的开头(或在多行模式下行的开头,即紧随一换行符之后)  断言目标的结尾(或在多行模式下行的结尾,即紧随一换行符之前)  匹配除了换行符外的任意一个字符(默认情况下)  字符类定义开始  字符类定义结束  开始一个多选一的分支  子模式开始  子模式结