交互式表达式求值(支持多种类型的运算符)

2023-11-02 05:10

本文主要是介绍交互式表达式求值(支持多种类型的运算符),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

交互式表达式求值(支持多种类型的运算符)

一、说明

  1. 输入字符串为中缀表达式,无需转为后缀表达式

  2. 支持的运算符包括:

算术运算符:“+,-,*,/”

关系运算符:“>,<,>=,<=,=,!=”(注意等于运算符采用的是一个等号)

逻辑运算符:“&&,||”

单目运算符:“++,–”

  1. 支持大于10的数字,不支持负数操作数,但支持中间结果和返回值为负数
  2. 支持逗号分隔表达式
  3. 支持变量,程序运行期间,变量保持其值
  4. 以交互方式求值,按行输入表达式,回车显示其值

二、算法原理&步骤

本文算法对中缀表达式形式字符串进行求值,同时支持与或运算和逻辑运算(若含有关系运算符或者逻辑运算符,则输出为1或者0)。类似于加减乘除,将关系运算符和逻辑运算符看作优先级低的运算符进行处理,优先级:单目运算符>算术运算符>关系运算符>逻辑运算符。

步骤:

  1. 初始化两个空堆栈,一个存放操作数,一个存放运算符。
  2. 初始化一个Map,用于存储运行过程中的变量。
  3. 找到表达式中 '=‘和’,‘的位置,‘,’会被视为两个完整语句,‘=’会将左边和右边的分隔开来,’='左边作为待赋值的变量,‘=’右边视为待求值的表达式。
  4. . 对于每个表达式,从左至右扫描输入字符串,依次读取。
  • 4.1 若为操作数,则压入操作数栈;
  • 4.2 若为字母开头,则继续扫描,到下一个字母为操作符为止,都被视为变量,在Map中查找该变量的值,压入操作数栈。
  • 4.2 若为运算符,判断其优先级是否大于运算符栈栈顶元素优先级。若大于栈顶元素优先级,则直接压栈;否则,弹出栈顶元素operator,同时依次从操作数栈中弹出两个元素number1,number2,计算表达式(number2 operator number1)的值value,并将值value压入操作数栈。重复上述过程直至当前扫描的操作符优先级大于栈顶元素,然后将当前运算符压栈。
  1. 弹出运算符栈顶元素operator,判断是单目运算符还是双目运算符,单目运算符则弹出一个操作数,否则同时依次从操作数栈中弹出两个元素number1,number2,计算表达式(number2 operator number1)的值value,并将值value压入操作数栈。重复上述过程直至运算符栈为空。

  2. 此时操作数栈应该只有一个元素,即为表达式的值。

三、代码

#include <bits/stdc++.h>
using namespace std;
unordered_map<string,int> Map;/* 判断字符是否属于运算符 */
bool isOperator(char c)
{switch (c){case '-':case '+':case '*':case '/':case '%':case '<':case '>':case '=':case '!':case '&':case '|':return true;default:return false;}
}/* 获取运算符优先级 */
int getPriority(string op)
{int temp = -1;if (op == "++" || op == "--")temp = 5;else if (op == "*" || op == "/" || op == "%")temp = 4;else if (op == "+" || op == "-")temp = 3;else if (op == ">" || op == "<" || op == ">=" || op == "<="|| op == "=" || op == "!=")temp = 2;else if (op == "&&" || op == "||")temp = 1;return temp;
}
/** 返回一个两元中缀表达式的值* syntax: num_front op num_back* @param num_front: 前操作数* @param num_back: 后操作数* @param op: 运算符*/
int Calculate(int num_front, int num_back, string op)
{if (op == "+")return num_front + num_back;else if (op == "-")return num_front - num_back;else if (op == "*")return num_front * num_back;else if (op == "/")return num_front / num_back;else if (op == "%")return num_front % num_back;else if (op == "!=")return num_front != num_back;else if (op == ">=")return num_front >= num_back;else if (op == "<=")return num_front <= num_back;else if (op == "==")return num_front == num_back;else if (op == ">")return num_front > num_back;else if (op == "<")return num_front < num_back;else if (op == "&&")return num_front && num_back;else if (op == "||")return num_front || num_back;else if (op == "++")return num_front + 1;else if (op == "--")return num_front - 1;return 0;
}
/* 新运算符入栈操作 */
void addNewOperator(string new_op, stack<int>& operand_stack, stack<string>& operator_stack)
{while (!operator_stack.empty() && getPriority(operator_stack.top()) >= getPriority(new_op)){int num2 = operand_stack.top();operand_stack.pop();int num1 = operand_stack.top();operand_stack.pop();string op = operator_stack.top();operator_stack.pop();int val = Calculate(num1, num2, op);operand_stack.push(val);}operator_stack.push(new_op);
}int equalpos(string input)
{for (int i=1;i<input.size()-1;i++){if(input[i]=='=' && !isOperator(input[i-1]) && !isOperator(input[i+1])){return i;}}return -1;
}/* 字符串表达式求值* @param input: 输入的字符串* @param output: 表达式的值,若含有关系运算符则为1或者0* return 计算过程是否正常*/
bool ExpValue(string input_prev,int& output)
{int equal_pos = equalpos(input_prev);string input = "";string var = "";if(equal_pos==-1) input = input_prev;else {var = input_prev.substr(0,equal_pos);input = input_prev.substr(equal_pos+1,input_prev.size());}stack<int> operand_stack;stack<string> operator_stack;char prev = 0; // 上一个属于运算符的字符for (int i = 0; i < input.size(); i++){char c = input[i];// prev是否是一个完整运算符if (!isOperator(c) && prev){string new_op = string("").append(1, prev);addNewOperator(new_op, operand_stack, operator_stack);prev = 0;}// 数字if (isdigit(c)){int val_c = c - '0';if (i > 0 && isdigit(input[i - 1])){int top_num = operand_stack.top();top_num = top_num * 10 + val_c;operand_stack.pop();operand_stack.push(top_num);}elseoperand_stack.push(val_c);}//变量else if(c>='a' && c<='z' || c>='A' && c<='Z'){string s = ""; s = s+c;c = input[++i];while(c>='a' && c<='z' || c>='A' && c<='Z' || c>='0' && c<='9' || c=='_'){s += c;c = input[++i];}if(Map.find(s)!=Map.end()){operand_stack.push(Map[s]);i--;}}// 运算符字符else if (isOperator(c)){// 处理两字符运算符if (prev){string new_op = string("").append(1, prev).append(1, c);addNewOperator(new_op, operand_stack, operator_stack);prev = 0;}elseprev = c;}else if (c == '(')operator_stack.push("(");else if (c == ')'){// 处理括号内的运算符while (operator_stack.top()!="("){int num1 = operand_stack.top();operand_stack.pop();int num2 = operand_stack.top();operand_stack.pop();string op = operator_stack.top();operator_stack.pop();int val = Calculate(num2, num1, op);operand_stack.push(val);}operator_stack.pop(); // 弹出"("}}
//    assert(operand_stack.size() == operator_stack.size() + 1);// 弹出所有运算符while(!operator_stack.empty()){string op = operator_stack.top();operator_stack.pop();int num1,num2;if(op=="++" || op=="--"){  //单目运算符num1 = operand_stack.top();operand_stack.pop();num2 = 0;}else{num2 = operand_stack.top();operand_stack.pop();num1 = operand_stack.top();operand_stack.pop();}int val = Calculate(num1, num2, op);
//        cout<<num1<<op<<num2<<"="<<val<<endl;operand_stack.push(val);}if (operand_stack.size() == 1) {output = operand_stack.top();if(var!="")Map[var] = output;return true;}return false;
}vector<string> split(string s, char ch)
{vector<string> res;int n = s.size();int i = 0;while(i < n){int j = i + 1;while(j < n && s[j] != ch) ++j;res.push_back(s.substr(i, j - i));i = j + 1;}return res;
}void test()
{string s0 = "10-1*10+3%2";string s1 = "100 + (3-33)*2";string s2 = "20+1 >= 20 && 20+1 < 20";string s3 = "10>20 || 10/1>=5";string s4 = "a4=2";string s5 = "b=a4+3";string s6 = "b++";int ret = -1;if (ExpValue(s0, ret))cout << s0 << ": " << ret << endl;if (ExpValue(s1, ret))cout << s1 << ": " << ret << endl;if (ExpValue(s2, ret))cout << s2 << ": " << ret << endl;if (ExpValue(s3, ret))cout << s3 << ": " << ret << endl;if (ExpValue(s4, ret))cout << s4 << ": " << ret << endl;if (ExpValue(s5, ret))cout << s5 << ": " << ret << endl;if (ExpValue(s6, ret))cout << s6 << ": " << ret << endl;
}void Interactive_Calculator()
{while(true){char *s;int ret = -1;cout<<"&";  cin>>s;vector<string> exprs = split(s,',');for(int i=0;i<exprs.size();i++){ExpValue(exprs[i],ret);}cout<<ret<<endl;
//        if (ExpValue(s, ret))
//            cout << ret << endl;}
}int main()
{
//    test();Interactive_Calculator();return 0;
}

四、测试结果

image-20220918112217717

image-20220918112224791

这篇关于交互式表达式求值(支持多种类型的运算符)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/328342

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

【重学 MySQL】十九、位运算符的使用

【重学 MySQL】十九、位运算符的使用 示例检查权限添加权限移除权限 在 MySQL 中,位运算符允许你直接在整数类型的列或表达式上进行位级操作。这些操作对于处理那些需要在二进制表示上进行直接修改或比较的场景特别有用,比如权限管理、状态标记等。 &(位与) 对两个数的二进制表示进行位与操作。只有两个相应的二进制位都为 1 时,结果的该位才为 1,否则为 0。 |(位

如何掌握面向对象编程的四大特性、Lambda 表达式及 I/O 流:全面指南

这里写目录标题 OOP语言的四大特性lambda输入/输出流(I/O流) OOP语言的四大特性 面向对象编程(OOP)是一种编程范式,它通过使用“对象”来组织代码。OOP 的四大特性是封装、继承、多态和抽象。这些特性帮助程序员更好地管理复杂的代码,使程序更易于理解和维护。 类-》实体的抽象类型 实体(属性,行为) -》 ADT(abstract data type) 属性-》成

Java基础回顾系列-第三天-Lambda表达式

Java基础回顾系列-第三天-Lambda表达式 Lambda表达式方法引用引用静态方法引用实例化对象的方法引用特定类型的方法引用构造方法 内建函数式接口Function基础接口DoubleToIntFunction 类型转换接口Consumer消费型函数式接口Supplier供给型函数式接口Predicate断言型函数式接口 Stream API 该篇博文需重点了解:内建函数式

C语言程序设计(数据类型、运算符与表达式)

一、C的数据类型 C语言提供的数据类型: 二、常量和变量 2.1常量和符号常量 在程序运行过程中,其值不能被改变的量称为常量。 常量区分为不同的类型: 程序中用#define(预处理器指令)命令行定义变量将代表常量,用一个标识符代表一个常量,称为符合常量。 2.2变量 变量代表内存中具有特定属性的一个存储单元,用来存放数据,在程序运行期间,这些值是可以 改变的。 变

JavaSE(十三)——函数式编程(Lambda表达式、方法引用、Stream流)

函数式编程 函数式编程 是 Java 8 引入的一个重要特性,它允许开发者以函数作为一等公民(first-class citizens)的方式编程,即函数可以作为参数传递给其他函数,也可以作为返回值。 这极大地提高了代码的可读性、可维护性和复用性。函数式编程的核心概念包括高阶函数、Lambda 表达式、函数式接口、流(Streams)和 Optional 类等。 函数式编程的核心是Lambda

Golang支持平滑升级的HTTP服务

前段时间用Golang在做一个HTTP的接口,因编译型语言的特性,修改了代码需要重新编译可执行文件,关闭正在运行的老程序,并启动新程序。对于访问量较大的面向用户的产品,关闭、重启的过程中势必会出现无法访问的情况,从而影响用户体验。 使用Golang的系统包开发HTTP服务,是无法支持平滑升级(优雅重启)的,本文将探讨如何解决该问题。 一、平滑升级(优雅重启)的一般思路 一般情况下,要实现平滑

第二十四章 rust中的运算符重载

注意 本系列文章已升级、转移至我的自建站点中,本章原文为:rust中的运算符重载 目录 注意一、前言二、基本使用三、常用运算符四、通用约束 一、前言 C/C++中有运算符重载这一概念,它的目的是让即使含不相干的内容也能通过我们自定义的方法进行运算符操作运算。 比如字符串本身是不能相加的,但由于C++中的String重载了运算符+,所以我们就可以将两个字符串进行相加、但实际