# testnet **Repository Path**: XiangFugui/testnet ## Basic Information - **Project Name**: testnet - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2017-12-12 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Simple Calculator ### 项目介绍 能够实现实数的加减乘除运算,能实现阶乘、幂运算,支持括号、小数点等运算符,对非法输入的识别处理,能批量处理txt文件中的表达式。 ### 开发环境 * Visual Studio 2017 * C++ ### 技术点 * 栈的使用。 * 对非法输入的异常处理。 * 运算中负数的处理。 ### 说明 > 双击目录下的“Simple Calculator.exe” 文件即可打开程序。输入合法且本程序支持的表达式,按下回车即可返回计算结果。当在`输入计算式子:`栏中输入空字符(即直接按下回车)即可结束程序。输入"file",可以调用处理txt文件。 > 本文档会简要介绍设计思路,各个函数的说明,技术难点部分,实现的细节会注释在源码部分。 > 作者技术有限,很多概念不成熟,一定程度上破坏了软件规范性和计算效率。虽然程序经过多次修改,但也不能保证滴水不漏,希望大家能提供宝贵的建议。 ![image](image/part.gif) 图 1: 输入表达式 ![image](image/file1.gif) 图 2: 输入txt文件批量处理 ### 设计思路 定义两个栈,一个栈用于存放数字,另一个栈用于存放运算符。运算符栈事先存入`\0`作为“头哨兵”(作用之后再讲)。规定当前操作符和栈顶操作符的优先级。对于输入的表达式在字符串尾加上`\0`作为“尾哨兵”。 1. 从左至右扫描表达式,对于数字,进入数栈。 1. 对于运算符,处理较为复杂:比较当前 栈顶和当前扫描到的运算符(下简称:栈顶和当前): > 栈顶小于当前时,将当前操作符入栈,继续扫描下一个元素。 > 栈顶大于当前时,弹出栈顶运算符,同时数栈弹出相应数量的数字,与该弹出的运算符进行相应的运算,所得结果入数栈,继续扫描下一个元素。 > 栈顶等于当前时,弹出栈顶运算符,继续扫描下一个元素。 3. 重复上述操作至运算符栈为空,取数栈顶部元素作为运算结果返回 对于负数(符号的)的处理相当特殊:我们定义输入的表达式和表达式中括号里的表达式都算一个式子。在这些式子的开头检测首个字符是否是符号'-',如果是,则认为效果等同于将符号改为乘号'*',并在数栈中添加'-1'。 #### 举例:`3+2*(7-4)` 数栈存入`3`;运算符栈存入`+`;数栈存入`2`;读到`*`,与`+`比较优先级,按规则`*`入栈;读到`(`,与`*`比较优先级,按规则`(`入栈;数栈存入`7`;`-`与`(`比较,入栈;读到`)`,比较栈顶的`-`后,弹出`-`,弹出数栈的4和7,计算得`3`,所得结果入数栈;`)`与`(`比较,`(`出栈,读到“尾哨兵”`\0`;`\0`与`*`比较,弹出`*`,弹出`3`和`2`,所得结果`6`入数栈na;`\0`与`+`比较,弹出`+`,弹出`6`与`3`,数栈存入`9`,`\0`与`\0`,弹出了栈顶的`\0`,此时运算符栈为空(头哨兵与尾哨兵完成了工作)。读取数栈顶部元素`9`即为所求结果。 ### 函数说明 ### `Mystack.h`文件下的声明 `Mystack`继承于标准库中`stack`,重载了`pop()`让栈弹出顶部元素并返回,重载的目的只是为了代码的可读性,从性能上来说有更好的方法 ### `priority.h`文件下的声明 #### const char prim[][] 定义了栈顶运算符和当前运算符的优先级。注意同一种字符,当它是栈顶运算符或当前运算符时优先级是不同的。 #### symbol opToRank(char) 将参数的字符转换为对应枚举常量(enumeration)中枚举子(enumerator),枚举子会隐式地被赋值。如本程序的symbol中:ADD会被隐式地赋为0,SUB会被隐式地赋为1…… #### char compare(char top, char cur) 调用 `opToRank(char)`函数,在数组`prim`得到栈顶操作符和当前栈顶操作符优先级比较的结果,返回'<','>','='。 ### Cacl.h下声明函数 #### double charToNum(char*) 字符转化为数字。通过ASCII码的相减,再强制类型转化成double。 #### double readNum(char* &) 处理表达式中数字,甚至是多个位数或含有小数的数字。传入一个指向数字的指针的引用,检查其后若干字符,解析出当前的操作数。 #### double calcu(double num, char op) #### double calcu(double num1, char op, double num2) 对应一元运算符和二元运算符,调用与运算符相应的计算函数。 #### void ifNextIsNega(char* &) 针对负号的处理。对于每个式子的首字符,若是'-',则将符号改为乘号'*',并在数栈中添加'-1'。 #### double calculate(string&) 接受输入的表达式。对定义的数栈和操作符栈做初始的处理。检查表达式每个式子的首字符是否是'-',做相应处理。指针char* p从左到右扫描表达式,相应的处理在设计思路中已经描述过了,源码中也有大量注释,这里不再赘述。 ### 参考 邓俊辉《*数据结构(C++语言版)*》*第四章 栈与队列* ### 总结 这是我做的第一个小项目,经验十分不足,但好在这个程序并不算很困难,勉强完成。要承认的是,算法的核心是参考了《*数据结构(C++语言版)*》,本文档对于这部分解释得也不甚清晰,感兴趣的朋友可以参考这本书。 不管是从最初只能实现加减乘除到现在可以支持多个常用运算,我都学到了很多。 我是想着通过分离式编译增强代码的可读性的,不过实际做出来很凌乱,弄巧成拙了。异常处理和用户交互虽然够用,但总觉得很欠缺。内存管理,性能什么的也没特别注意。欢迎各位朋友来码云私信我。