如何正确将含√、Σ等符号函数的无括号表达式转换为RPN形式
问题背景
我目前在实现符号化函数(使用√、Σ这类专用符号表示的函数)到RPN(逆波兰表达式)的转换逻辑,现有实现存在转换错误:
- 测试表达式
√√100会被错误转换为√100√ - 预期正确转换结果为
100√√
虽然可以通过显式加括号写成√(√(100))的形式得到正确结果,但这种写法会让复杂表达式变得非常冗余,因此需要在不额外添加括号的前提下实现正确转换。
现有实现代码
std::wstring revpol(std::wstring parsedf, std::wstring mtok) { std::wstring tparsed; std::wistringstream mplit(parsedf); while(mplit >> mtok) { mspec = false; if(rxm(mtok,pnum)) { tparsed += mtok + L" "; } else{ if(Pval[mtok] == 0) { mspec = true; } if(mspec){ if(mtok == L"(") { preop.push({mtok,Pval[mtok]}); mspec = false; } else if(mtok == L")") { while(preop.top().first != L"(") { tparsed += preop.top().first + L" "; preop.pop(); } if(preop.top().first == L"(") { preop.pop(); } mspec = false; } else if(mtok == L",") { tparsed += mtok + L" "; mspec = false; } else{ if(mtok.back() == '$'){ // to be changed Tmtok = mtok; mtok.pop_back(); Pval.insert({mtok,3}); mtok = Tmtok; mspec = false; std::cout<<preop.size()<<std::endl; while(!preop.empty() && preop.top().second >= 3 && mtok != L"^") { tparsed += preop.top().first + L" "; preop.pop(); } preop.push({mtok,3}); } else{ tparsed += mtok + L" "; mspec = false; } } } } } // 原代码未贴出遍历完成后清空运算符栈的逻辑 }
错误原因
当前实现基于调度场算法,但没有正确处理一元前缀运算符的逻辑:
- 没有对一元前缀运算符(如√)做单独的属性标记,和二元运算符混用了优先级、结合性规则
- 运算符出栈判断逻辑错误:对于右结合的运算符(包括前缀一元运算符、幂运算符^),不应该在栈顶优先级大于等于当前优先级时出栈,仅当栈顶优先级大于当前优先级时才需要出栈
- 对于不带
$标记的运算符,现有逻辑直接将其输出到结果串,完全跳过了运算符栈的调度流程,是导致√√100转换错误的核心原因之一
修复方法
- 单独定义一元前缀运算符的属性:给√这类一元前缀运算符设置高于所有二元运算符的优先级(比如设为4),明确标记为右结合
- 移除“非标记运算符直接输出到结果”的分支,所有运算符都必须进入运算符栈走统一的调度逻辑
- 调整入栈前的弹出规则:
- 遇到左结合运算符,循环弹出栈中优先级大于等于当前运算符的元素到结果串
- 遇到右结合运算符(含一元前缀运算符、^),循环弹出栈中优先级严格大于当前运算符的元素到结果串
- 表达式遍历完成后,把运算符栈中剩余的所有元素依次弹出到结果串
修复后√√100的处理流程为:
- 读入第一个√,栈空直接压入运算符栈
- 读入第二个√,栈顶为同优先级右结合运算符,不弹出,直接压栈
- 读入数字100,直接追加到结果串,当前结果为
100 - 表达式遍历完成,依次弹出栈内的两个√追加到结果,最终得到
100√√,符合预期。
内容的提问来源于stack exchange,提问作者bigbroin
相关产品推荐
相关产品推荐

