013-正规式与正则表达式
难度:🟡 | 预计时间:90分钟 | 前置:012-非确定有限自动机NFA
学习目标
- 理解正规式(正则表达式)的形式化定义和语法
- 掌握正规式的基本运算:连接、并、Kleene星
- 学会正规式与有限自动机的等价性证明
- 理解正规式在编译器词法分析中的应用
- 掌握正规式的优化和化简技术
内容正文
正规式的形式化定义
正规式(Regular Expression)是描述正则语言的代数表示法,它提供了一种简洁的方式来表达字符串模式。
递归定义
设Σ是有限字母表,Σ上的正规式定义如下:
-
基础情况:
- ∅(空集)是正规式
- ε(空字符串)是正规式
- 对于任意a ∈ Σ,a是正规式
-
递归情况:
- 如果r和s是正规式,则(r|s)是正规式(并运算)
- 如果r和s是正规式,则(rs)是正规式(连接运算)
- 如果r是正规式,则r*是正规式(Kleene星运算)
语义定义
每个正规式r定义一个语言L®:
- L(∅) = ∅
- L(ε) = {ε}
- L(a) = {a},其中a ∈ Σ
- L(r|s) = L® ∪ L(s)
- L(rs) = L® · L(s) = {xy | x ∈ L®, y ∈ L(s)}
- L(r*) = (L®)* = ∪_{i≥0} (L®)^i
正规式的语法和运算
运算符优先级
# 文件路径: examples/regex_precedence.py
# 正规式运算符优先级演示
class RegexPrecedence:
"""正规式运算符优先级和结合性"""
PRECEDENCE = {
'*': 3, # Kleene星,最高优先级
'+': 3, # 正闭包
'?': 3, # 可选
'CONCAT': 2, # 连接运算(隐式)
'|': 1 # 并运算,最低优先级
}
def __init__(self):
self.examples = [
# (表达式, 默认解析, 显式括号)
('ab|c', '(ab)|c', '((a)(b))|(c)'),
('a|bc', 'a|(bc)', '(a)|((b)(c))'),
('ab*', 'a(b*)', '(a)((b)*)'),
('(a|b)*', '(a|b)*', '((a)|(b))*'),
('a|b*c', 'a|(b*c)', '(a)|((b*)(c))'),
('ab|cd*', '(ab)|(cd*)', '((a)(b))|((c)(d*))'),
]
def demonstrate_precedence(self):
"""演示运算符优先级"""
print("=== 正规式运算符优先级演示 ===")
print("优先级(高到低): * + ? > 连接 > |")
print()
for expr, default, explicit in self.examples:
print(f"表达式: {
expr}")
print(f" 默认解析: {
default}")
print(f" 完全展开: {
explicit}")
print()
def show_associativity(self):
"""演示结合性"""
print("=== 运算符结合性 ===")
associativity_examples = [
('a|b|c', '((a|b)|c)', '左结合'),
('abc', '((ab)c)', '左结合'),
('a***', '((a*)*)*', '左结合'),
]
for expr, parsed, rule in associativity_examples:
print(f"{
expr} → {
parsed} ({
rule})")
print()
# 演示优先级和结合性
prec_demo = RegexPrecedence()
prec_demo.demonstrate_precedence()
prec_demo.show_associativity()
扩展运算符
# 文件路径: examples/extended_regex_operators.py
# 扩展正规式运算符实现
class ExtendedRegexOperators:
"""扩展的正规式运算符"""
def __init__(self):
self.operators = {
'+': '正闭包(一次或多次)',
'?': '可选(零次或一次)',
'{n}': '精确重复n次',
'{n,m}': '重复n到m次',
'[abc]': '字符类(a或b或c)',
'[^abc]': '否定字符类(除a、b、c外)',
'.': '任意字符(通配符)',
'^': '行首锚点',
'$': '行尾锚点'
}
def expand_to_basic(self, extended_expr):
"""将扩展运算符转换为基本正规式"""
expansions = {
'a+': 'aa*', # 正闭包
'a?': '(ε|a)', # 可选
'a{3}': 'aaa', # 精确重复
'a{2,4}': 'aa(ε|a)(ε|a)', # 范围重复(简化)
'[abc]': '(a|b|c)', # 字符类
'[^abc]': '(d|e|f|...)', # 否定字符类(示例)
}
print("=== 扩展运算符到基本正规式的转换 ===")
for ext, basic in expansions.items():
print(f"{
ext} → {
basic}")
print()
return expansions.get(extended_expr, extended_expr)
def demonstrate_equivalences(self):
"""演示等价转换"""
equivalences = [
('a+', 'aa*', '正闭包等价于:至少一个a'),
('a?', '(ε|a)', '可选等价于:空或一个a'),
('(a|b)+', '(a|b)(a|b)*', '正闭包的复合形式'),
('[0-9]', '(0|1|2|3|4|5|6|7|8|9)', '数字字符类'),
('a{2,3}', 'aa(ε|a)', '有界重复'),
]
print("=== 扩展运算符等价性 ===")
for original, equivalent, description in equivalences:
print(f"{
original} ≡ {
equivalent}")
print(f" 说明: {
description}")
print()
# 演示扩展运算符
ext_ops = ExtendedRegexOperators()
ext_ops.demonstrate_equivalences()
正规式与有限自动机的等价性
Kleene定理
Kleene定理:一个语言是正则语言当且仅当它可以用正规式表示。
证明分为两个方向:
- 正规式 → NFA(Thompson构造法)
- DFA → 正规式(状态消除法)
状态消除法:DFA到正规式
# 文件路径: examples/state_elimination.py
# 状态消除法:将DFA转换为正规式
class StateElimination:
"""状态消除法实现"""
def __init__(self, dfa):
self.dfa = dfa
self.transitions = {
} # 广义转换函数,值为正规式
self.states = set(dfa.states)
# 初始化转换表
self.initialize_transitions()
def initialize_transitions(self):
"""初始化转换表为正规式"""
# 为所有状态对初始化空转换
for q1 in self.states:
for q2 in self.states:
self.transitions[(q1, q2)] = '∅'
# 设置实际转换
for (state, symbol), next_state in self.dfa.transitions.items():
key = (state, next_state)
if self.transitions[key] == '∅':
self.transitions[key] = symbol
else:
# 合并多个转换
self.transitions[key] = f"({
self.transitions[key]}|{
symbol})"
# 设置自环(空字符串转换)
for state in self.states:
if self.transitions[(state, state)] == '∅':
self.transitions[(state, state)] = 'ε'
else:
self.transitions[(state, state)] = f"(ε|{
self.transitions[(state, state)]})"
def eliminate_state(self, state_to_eliminate):
"""消除指定状态"""
print(f"\n消除状态: {
state_to_eliminate}")
# 获取所有相关转换
incoming = [] # 进入被消除状态的转换
outgoing = [] # 从被消除状态出发的转换
self_loop = self.transitions.get((state_to_eliminate, state_to_eliminate), 'ε')
for state in self.states:
if state != state_to_eliminate:
in_trans = self.transitions.get((state, state_to_eliminate), '∅')
if in_trans != '∅':
incoming.append((state, in_trans))
out_trans = self.transitions.get((state_to_eliminate, state), '∅')
if out_trans != '∅':
outgoing.append((state, out_trans))
print(f" 进入转换: {
incoming}")
print(f" 自环: {
self_loop}")
print(f" 出发转换: {
outgoing}")
# 更新转换表
new_tran


1万+

被折叠的 条评论
为什么被折叠?



