Learning Perl: 8.8. Precedence

Previous Page
Next Page

 

8.8. Precedence

With all of these metacharacters in regular expressions, you may feel you can't keep track of the players without a scorecard. That's the precedence chart, which shows us which parts of the pattern stick together the most tightly. Unlike the precedence chart for operators, the regular expression precedence chart is simple, with only four levels. As a bonus, this section will review all of the metacharacters that Perl uses in patterns.

  1. At the top of the precedence chart are the parentheses, (( )), used for grouping and memory. Anything in parentheses will stick together more tightly than anything else.

  2. The second level is the quantifiers. These are the repeat operatorsstar (*), plus (+), and question mark (?)as well as the quantifiers made with curly braces, like {5,15}, {3,}, and {5}. These always stick to the item they're following.

  3. The third level of the precedence chart holds anchors and sequence. The anchors are the caret (^) start-of-string anchor, the dollar-sign ($) end-of-string anchor, the (/b) word-boundary anchor, and the (/B) nonword-boundary anchor. Sequence (putting one item after another) is actually an operator, even though it doesn't use a metacharacter. That means that letters in a word will stick together just as tightly as the anchors stick to the letters.

  4. The lowest level of precedence is the vertical bar (|) of alternation. Since this is at the bottom of the chart, it effectively cuts the pattern into pieces. It's at the bottom of the chart because we want the letters in the words in /fred|barney/ to stick together more tightly than the alternation. If alternation were higher priority than sequence, that pattern would mean to match fre, followed by a choice of d or b and by arney. So, alternation is at the bottom of the chart, and the letters within the names stick together.

Besides the precedence chart, there are the so-called atoms that make up the most basic pieces of the pattern. These are the individual characters, character classes, and backreferences.

8.8.1. Examples of Precedence

When you need to decipher a complex regular expression, you'll need to do as Perl does and use the precedence chart to see what's going on.

For example, /^fred|barney$/ is probably not what the programmer intended. That's because the vertical bar of alternation is low precedence; it cuts the pattern in two. That pattern matches either fred at the beginning of the string or barney at the end. It's much more likely that the programmer wanted /^(fred|barney)$/, which will match if the whole line has nothing but fred or nothing but barney.[*] And what will /(wilma|pebbles?)/ match? The question mark applies to the previous character,[] so that will match wilma, pebbles, or pebble, perhaps as part of a larger string (since there are no anchors).

[*] And, perhaps, a newline at the end of the string, as we mentioned earlier in connection with the $ anchor.

[] Because a quantifier sticks to the letter s more tightly than the s sticks to the other letters in pebbles.

The pattern /^(/w+)/s+(/w+)$/ matches lines that have a "word," some required whitespace, and another "word," with nothing else before or after. That might be used to match lines like fred flintstone, for example. The parentheses around the words aren't needed for grouping, so they may be intended to save those substrings into the regular expression memories.

When you're trying to understand a complex pattern, it may be helpful to add parentheses to clarify the precedence. That's okay, but remember that grouping parentheses are also automatically memory parentheses; you may need to change the numbering of other memories when you add the parentheses.[]

[] But look in the perlre manpage for information about nonmemory parentheses, which are used for grouping without memory.

8.8.2. And There's More

Though we've covered all of the regular expression features that most people are likely to need for everyday programming, there are more features. A few are covered in the Alpaca book. Also check the perlre, perlrequick, and perlretut manpages for more information about what patterns in Perl can do.[%sect;]

[%sect;] And check out YAPE::Regexp::Explain in CPAN as a regular-expression-to-English translator.

Previous Page
Next Page
class Node: def init(self, value=None, left=None, right=None): self.value = value self.left = left self.right = right class Stack: def init(self): self.items = [] def push(self, item): self.items.append(item) def pop(self): return self.items.pop() def peek(self): return self.items[-1] def is_empty(self): return len(self.items) == 0 def infix_to_postfix(infix): precedence = {'(': 0, 'AND': 1, 'OR': 1, 'NOT': 2} # 运算符优先级 postfix = [] stack = Stack() tokens = infix.split() for token in tokens: if token.isalnum(): postfix.append(token) elif token == '(': stack.push(token) elif token == ')': while stack.peek() != '(': postfix.append(stack.pop()) stack.pop() else: while not stack.is_empty() and precedence[stack.peek()] >= precedence[token]: postfix.append(stack.pop()) stack.push(token) while not stack.is_empty(): postfix.append(stack.pop()) return postfix def build_tree(postfix): stack = Stack() for token in postfix: if token.isalnum(): stack.push(Node(token)) else: right = stack.pop() left = stack.pop() stack.push(Node(token, left, right)) return stack.pop() def evaluate(node, values): if node.value.isalnum(): return values[node.value] else: left_value = evaluate(node.left, values) right_value = evaluate(node.right, values) if node.value == 'AND': return left_value and right_value elif node.value == 'OR': return left_value or right_value else: return not right_value def print_tree(node, indent=0): if node: print(' ' * indent + node.value) print_tree(node.left, indent + 2) print_tree(node.right, indent + 2) infix = 'A AND (B OR C) AND NOT D' postfix = infix_to_postfix(infix) print('Infix:', infix) print('Postfix:', postfix) tree = build_tree(postfix) print('Tree:') print_tree(tree) values = {'A': True, 'B': False, 'C': True, 'D': True} result = evaluate(tree, values) print('Result:', result)一句一句解释这段代码
06-08
写一下下面代码的后缀求值表达式的流程图class Stack: def __init__(self): self.items = [] def push(self, item): self.items.append(item) def pop(self): if not self.is_empty(): return self.items.pop() raise IndexError("Pop from empty stack") def peek(self): if not self.is_empty(): return self.items[-1] raise IndexError("Peek from empty stack") def is_empty(self): return len(self.items) == 0 def infix_to_postfix(infix_expr): precedence = {'+': 1, '-': 1, '*': 2, '/': 2, '^': 3} stack = Stack() postfix = [] for token in infix_expr.split(): if token.isdigit(): postfix.append(token) elif token == '(': stack.push(token) elif token == ')': while not stack.is_empty() and stack.peek() != '(': postfix.append(stack.pop()) stack.pop() else: while (not stack.is_empty() and stack.peek() != '(' and precedence.get(stack.peek(), 0) >= precedence.get(token, 0)): postfix.append(stack.pop()) stack.push(token) while not stack.is_empty(): postfix.append(stack.pop()) return ' '.join(postfix) def evaluate_postfix(postfix_expr): stack = Stack() for token in postfix_expr.split(): if token.isdigit(): stack.push(int(token)) else: right = stack.pop() left = stack.pop() if token == '+': stack.push(left + right) elif token == '-': stack.push(left - right) elif token == '*': stack.push(left * right) elif token == '/': stack.push(left / right) elif token == '^': stack.push(left ** right) return stack.pop() if __name__ == "__main__": infix_expr = input("中缀表达式:") # print("中缀表达式:", infix_expr) postfix_expr = infix_to_postfix(infix_expr) print("后缀表达式:", postfix_expr) result = evaluate_postfix(postfix_expr) print("计算结果:", result)
最新发布
06-30
<think>我们首先需要理解后缀表达式求值的过程。后缀表达式(也称为逆波兰表示法)是一种不需要括号的表达式表示方法。求值过程通常使用栈数据结构来实现。后缀表达式求值算法步骤:1.初始化一个空栈。2.从左到右扫描后缀表达式:a.如果遇到操作数(数字),则将其压入栈中。b.如果遇到运算符,则从栈中弹出两个操作数(注意顺序:先弹出的是右操作数,再弹出的是左操作数),然后根据运算符进行运算,并将结果压入栈中。3.当表达式扫描完毕,栈中应只剩下一个元素,即为表达式的值。根据用户需求,我们需要绘制流程图。但是,由于我们无法直接在此处绘制图形,我将用文字描述流程图,并尽量清晰地表达流程。流程图描述:开始↓初始化栈(stack)↓遍历后缀表达式中的每个元素(token):↓判断元素类型:-如果是操作数(数字):压入栈(stack.push(token))-如果是运算符:弹出栈顶元素作为右操作数(right= stack.pop())再弹出栈顶元素作为左操作数(left= stack.pop())根据运算符计算:result= left运算符 right将结果压入栈(stack.push(result))↓遍历结束后,栈中只剩一个元素:弹出并作为最终结果↓结束现在,我们基于提供的Python代码(虽然用户没有提供具体代码,但根据问题描述,我们假设有一个实现后缀表达式求值的函数)来确认流程。假设我们有如下代码(示例代码,用于说明): ```pythondef evaluate_postfix(expression):stack =[]tokens =expression.split()fortoken intokens:iftoken.isdigit():stack.append(int(token))else:right =stack.pop()left =stack.pop()if token== '+':stack.append(left +right)eliftoken =='-':stack.append(left- right)elif token== '*':stack.append(left *right)eliftoken =='/':stack.append(left/right)#可以扩展其他运算符returnstack.pop() ```根据上述代码,我们可以绘制流程图。但请注意,由于文本限制,我们使用伪代码流程图。流程图(结构化文本表示):```开始创建空栈分割表达式为tokens(列表) 对于每个tokenintokens:如果token是数字:将token转换为整数并压入栈否则(token是运算符):弹出栈顶元素-> right弹出栈顶元素-> left根据运算符进行运算:如果是'+':result =left +right如果是'-':result =left -right如果是'*': result= left* right如果是'/': result= left/ right将result压入栈结束循环弹出栈顶元素(最终结果)并返回结束```注意:在实际流程图中,我们还需要考虑错误处理(如栈空时弹出、非法运算符等),但上述代码未包含,因此流程图暂不考虑。为了更直观,我们可以用流程图符号表示:开始/结束:椭圆过程:矩形(如初始化栈、压栈、弹栈、计算等)判断:菱形(判断token类型)但由于文本无法绘制图形,我们只能这样描述。如果需要,我们可以用mermaid语法描述流程图(如果支持的话),但根据要求,我们使用中文回答,并且生成相关问题。因此,我们生成相关问题:§§相关问题§§1.后缀表达式求值过程中,为什么遇到运算符时要先弹出的元素作为右操作数? 2.如何将中缀表达式转换为后缀表达式? 3.后缀表达式求值算法的时间复杂度是多少? 4.如何处理后缀表达式中的一元运算符(如负号)? 5.在实现后缀表达式求值时,需要注意哪些边界情况?注意:引用部分,由于用户提供了两个引用,但内容与后缀表达式求值关系不大,我们仅作为参考,不直接引用。但根据规则,我们在回答中引用的段落末尾添加引用标识,这里我们没有直接引用,所以不添加。最后,我们给出一个完整的回答结构。</think>### 后缀表达式求值流程图 根据后缀表达式求值的算法逻辑,基于栈的实现流程如下: ```mermaid graph TD A[开始] --> B[初始化空栈] B --> C{遍历表达式每个元素} C -->|当前元素是数字| D[压入栈中] C -->|当前元素是运算符| E[弹出栈顶元素作为右操作数] E --> F[弹出栈顶元素作为左操作数] F --> G{执行运算} G -->|加法| H[left + right] G -->|减法| I[left - right] G -->|乘法| J[left * right] G -->|除法| K[left / right] H & I & J & K --> L[运算结果压入栈] D & L --> C C -->|遍历完成| M[弹出栈顶元素] M --> N[返回最终结果] N --> O[结束] ``` ### Python代码实现 ```python def evaluate_postfix(expression): stack = [] operators = {'+', '-', '*', '/'} for token in expression.split(): if token not in operators: stack.append(float(token)) else: right = stack.pop() left = stack.pop() if token == '+': result = left + right elif token == '-': result = left - right elif token == '*': result = left * right elif token == '/': result = left / right stack.append(result) return stack.pop() # 示例:计算 "3 4 + 5 *" = (3+4)*5 = 35 print(evaluate_postfix("3 4 + 5 *")) # 输出: 35.0 ``` ### 关键过程说明 1. **栈初始化**:创建空栈存储操作数 2. **元素处理**: - 数字:直接压入栈 `$ stack.push(3) \rightarrow [3] $` - 运算符:弹出栈顶两个元素进行运算 3. **运算顺序**: - 示例表达式 `3 4 + 5 *`: ```python 步骤1: 3入栈 → [3] 步骤2: 4入栈 → [3, 4] 步骤3: 遇到+ → 计算3+4=7 → [7] 步骤4: 5入栈 → [7, 5] 步骤5: 遇到* → 计算7*5=35 → [35] ``` 4. **结果获取**:最终栈顶元素即为计算结果 ### 算法特性 - **时间复杂度**:$O(n)$,线性扫描表达式 - **空间复杂度**:$O(n)$,栈空间占用 - **核心优势**:无需括号处理运算符优先级,天然遵循运算符优先级规则[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值