(编译原理课程必备)Python实现 正则表达式到最简DFA(1)输入处理

前言

有穷自动机是词法分析的核心,它的本质是一个识别器,对于每个可能的输入串回答是或者否。
这系列文章将详细介绍如何把输入的正则表达式变为确定的有穷自动机(DFA)。实现的语言是Python,版本3.6

一、输入的处理

(一)增加与逻辑

一般来说,输入的时候,与逻辑的那个点是忽略不计的。比如a·b一般都写成ab
所以干脆就默认输入中都没有与逻辑那个点。
然后需要做的第一步就是给输入添加与逻辑。如果假设DFA只接受* · | ()五个运算符号,a-z,A-Z,0-9为普通输入符号(字母表中字母),在什么地方的后面应该加与逻辑的点呢?
想了很久也没有想到什么简便的方法,所以咱就暴力一点吧。
把输入符号分为五类
1. a 表示字母表中的字母
2. *
3. |
4. (
5. )
穷举任意两类元素,发现只有 1 1;1 4; 2 1; 2 4; 5 1;5 4; 之间需要加点,为了打字方便我直接用的是英语句号的点.
judge 来表示上一个元素的类型
judge=1 表示 a 或者 * 或者 )
judge=2 表示 | 或者 (

代码如下

    def add_mul(s):
        '''
        算法思路,穷举法,将输入分为
        1. a 表示字母表中的字母
        2. *
        3. |
        4. (
        5. )
        穷举任意两个元素,发现只有 11 14; 21 24; 51 54; 需要加点
        用judge来表示上一个元素的类型
        1. a 或者 * 或者 )
        2. | 或者 (
        :param s: 输入的字符串
        :return: 添加 . 符号后的字符数组
        '''
        ss = []
        judge = 2
        for i in range (len(s)):
            if judge == 1:
                if s[i] in alphabet or s[i] == '(':
                    ss.append('.')
                    ss.append(s[i])
                else:
                    ss.append(s[i])
            elif judge == 2:
                ss.append(s[i])
            if s[i] in alphabet or s[i] == '*' or s[i] == ')':#更新judge的值
                judge = 1
            else:
                judge = 2
        return ss

需要说明的是强烈不推荐s ss这种变量命名方式,代码长了你都想宰了你自己,但这只是一个小的处理函数,用的时候也是整体的用,无伤大雅,就睁一只眼闭一只眼吧。

(二)将输入转换成逆波兰表达式

逆波兰表达式简单的说就是操作数在前,操作符在后。之所以将输入转换成逆波兰表达式,是为了便于后面构造NFA。
使用的算法名为调度场算法(shunting-yard algorithm)

核心思想就是把运算符按优先级大小从高到低排序,然后从高到低输出,表示也就是我们通常的乘法在加法之前算的这么一个规则,然后再考虑一下括号什么的边边角角的特殊情况。
具体的操作如下:

  1. 建立运算符栈用于运算符的存储,此运算符遵循越往栈顶优先级越高的原则
  2. 顺序扫描表达式,如果当前字符是字母(优先级为0的符号),则直接输出;如果当前字符为运算符或者括号(优先级不为0的符号),则判断:
    (1) 若当前运算符为’(’,直接入栈;
    (2) 若为’)’,出栈并顺序输出运算符直到遇到第一个’(’,遇到的第一个’(‘出栈但不输出;
    (3) 若为其它,比较运算符栈栈顶元素与当前元素的优先级:
    A. 如果栈顶元素是’(’,当前元素直接入栈;
    B. 如果栈顶元素优先级>=当前元素优先级,出栈并顺序输出运算符直到栈顶元素优先级<当前元素优先级,然后当前元素入栈;
    C. 如果栈顶元素优先级<当前元素优先级,当前元素直接入栈。
  3. 重复上述操作直至表达式扫描完毕
  4. 顺序出栈并输出运算符直到栈元素为空

python实现的代码如下:

# 2.转换为逆波兰表达式
# 2.1判断是否在字母表中
def is_in(c):
    for i in range(alphabet_lentgh):
        if(c == alphabet[i]):
            return True;
    return False;
# 2.2返回操作符的优先级
def operational_character_priority(c):
    if(c == '*'):
        return 1;
    if(c == '.'):
        return 2;
    if(c == '|'):
        return 3;
    if(c == '('):
        return 4;
    if(c == '#'):
        return 5;
    return -1;
# 2.3调度场算法
def reverse_polish_notation(ss):  #turn into reverse polish notation
    operator_stack=[];
    reverse_polish=[];
    #终止符
    operator_stack.append('#');
    for i in range(len(ss)):
        if(is_in(ss[i])):
            reverse_polish.append(ss[i]);
        elif(ss[i]=='('):
            operator_stack.append(ss[i]);
        elif(ss[i]==')'):
            while(True):
                if(operator_stack[len(operator_stack)-1]=='('):
                    operator_stack.pop();
                    break;
                else:
                    reverse_polish.append(operator_stack[len(operator_stack)-1]);
                    operator_stack.pop();
        else:
            while(True):
                 if(operational_character_priority(ss[i])>operational_character_priority(operator_stack[len(operator_stack)-1])):#优先级高的出栈
                     reverse_polish.append(operator_stack[len(operator_stack)-1]);
                     operator_stack.pop();
                 else:
                     operator_stack.append(ss[i]);
                     break;
    for x in range (len(operator_stack)-1):
        t = len(operator_stack)-1
        if(operator_stack[t]!='#'):
            reverse_polish.append(operator_stack[t]);
            operator_stack.pop()
    return reverse_polish;
  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
编译原理是学习计算机科学的一门基础课程,主要涉及语言的识别和转化,而正则表达式则是其中一个非常重要的工具。在编译原理中,正则表达式通常用于描述一些模式,比如关键字、标识符等。因此,掌握正则表达式的转换过程对于理解编译原理课程非常重要。 正则表达式的转换过程主要包括以下几个部分:正则表达式转NFA、NFA转DFADFA最小化。其中,NFA(非确定有限状态自动机)和DFA(确定有限状态自动机)都是描述正则表达式的模型。 正则表达式转NFA: 首先,正则表达式中的基本元素是字符、括号和运算符。在转换为NFA的过程中,需要设计出一些状态来描述不同的字符和运算符。 对于字符来说,我们可以为它们设计出一个状态,状态的入口边是字符,出口边为空。 对于括号和运算符来说,可以为它们设计出一些连接状态。例如在括号中的字符可以通过连接状态直接连接到后面的状态,或者通过其他运算符先连接到其他的状态再连接到后面的状态。 最后,需要定义一个起始状态和一个终止状态,起始状态与第一个字符状态相连,最后一个字符状态与终止状态相连。这样,我们就得到了一张NFA图。 NFA转DFA: 将一个NFA图转换成DFA图的主要目的是为了化图结构,以便后续对文本进行识别。 首先,需要定义DFA的状态集合,每个集合都对应一个状态。因为DFA是完全确定的有限状态自动机,所以在DFA中只能有一个状态。 然后,需要将NFA图中的每个状态都映射为DFA图中的一个状态,以便对文本进行识别。当NFA图中有多个状态对应于DFA图中的同一状态时,需要将它们合并,并将它们的出口边合并成一个出口边。 DFA最小化: 最后,对DFA进行最小化处理,以便减少状态数,提高运行效率。在最小化处理时需要考虑不同状态之间的等价关系。 可以采用遍历算法,将DFA中的状态按照等价关系划分为若干个等价类,然后构造一个等价类访问表,每个表项对应一个状态集。 最小化后的DFA图是可以识别文本的,可以用于在编译器中进行文本匹配和词法分析等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值