zoukankan      html  css  js  c++  java
  • 命令行计算器(一)

    习惯在Matlab里面计算,不太喜欢系统自带的计算器,但Matlab每次启动都比较慢。对于简单的计算,用不着Matlab,一直希望有一个简单版本的Matlab。一次看到OSChina上有分享一个简单的命令行计算器,觉得这个不错,不过功能仅限于数字的四则运算,于是想着可不可以写一个功能在稍微强一点的命令行计算器。于是就有了这个项目,称之为Command Line Calculator(CLC)。

    希望实现的功能,

    1. 数值运算,整数和浮点数;

    2. 变量赋值,表达式运算;

    3. 数学函数;

    4. 其他。

    网上已经有些计算器或者是数学表达式运算的程序了,上面的功能也有人实现了的。所以我在做一边,一是练习一下编程,另一方面实现自己喜欢的风格。

    当然,也没打算一下子就把它做完,计划是三个阶段,逐步实现上面的功能。目前已经实现了最基本的功能,数值运算。下面总结一下实现过程。

    我觉得实现过程中有两个问题,一个是数值的提取,整数和浮点数。这里采用stringstream提取数值。读取输入表达式的时候,运算符或者括号是很容易识别的,就是字符比较,当字符不是运算符的时候,把字符放入stringstream,当遇到运算符的时候,就可以把stringstream里面的数值取出来,并把stringstream清空。另一个问题是数值的计算,因为运算符有优先级,还包括括号。网上有两种方式实现,一种是二叉树表示数学表达式,另一种是使用堆栈。我的实现过程中使用堆栈。用两个堆栈,一个存放数值,一个存放运算符。如果当前字符是(,那么直接将其放入运算符堆栈,如果是),那么将运算符堆栈进行计算,直到遇到(,两个括号匹配相消,如果是其它运算符,则比较这个运算符和上一个运算符之间的优先级关系,如果这次优先,则把这次的运算法放入堆栈,因为它还有一个操作数在右边还没有读到,如果上一个运算符的优先级高,那么可以完成上一次的运算。

    需要注意的是,因为是对输入表达式逐个字符处理,当处理最后一个字符时,如果是数字,需要将最后一个数字放入数值堆栈。并且一般情况下,数值堆栈和运算符堆栈不为空,需要继续进行计算。

    最终代码如下,

    /* cmd_calculator.cpp 
     * a simple implementation of cmd calculator using stack
     * author: Frandy.CH
     * data: 2012-05-03
     */
     
     /* ref:
      *        http://kb.cnblogs.com/a/1211638/
      *        http://www.oschina.net/code/snippet_190747_6535
      * more advanced:
      *        http://lony1107.blog.51cto.com/374424/78727
      *        http://blog.sina.com.cn/s/blog_67a1dd830100of48.html
      *        http://www.chinaitpower.com/A/2002-02-12/13734.html
      */
     
     /*
      * operator allowed: ( ) + - * /
      * assign operator different level, ( ) -1, + - 0, * / 1.
      * evaluation condition: 
      *     the current operator is not "", which is the end sign
      *        the num in the num stack more than 2
      *        the level of operator at the stack top is higher than the operatot to be push into stack
      * so there are two stacks, one is for number, the other is for operator
      */
      
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <stack>
    using namespace std;  
     
    // 输出程序的简单描述
    void description() 
    {
        cout << " Command Line Calculator v1.0" << endl;
        cout << " Author: Frandy.CH" << endl;
        cout << " This is a very simple command line calculator,\n which only support +,-,*,/,(,) and numbers, including int and float." << endl;
        cout << " You can input expression after the promt \'>\'" << endl;
        cout << " And you can exit by input \'q\' or \"exit\"" << endl;
    }
    
    // 输出提示符
    void promt() 
    {
        cout << ">";
    }
    
    // 输出推出程序提示信息
    void bye()
    {
        cout << "Bye." << endl;
    }
    
    // 比较当前运算符和上一次运算符的优先级
    int cmpop(char c1,char c2)
    {
        switch(c1)
        {
            case '-':
            case '+':
                return (c2=='(')?0:1;
                break;
            case '/':
            case '*':
                if(c2=='*' || c2=='/')
                    return 1;
                else
                    return 0;
                break;
            default:
                cout << "error:unknown op" << endl;
        }
    }
    
    // 利用stringstream提取数值放入数值堆栈
    void getNum(stringstream& ss, stack<double>& value)
    {
        double st;
        ss >> st;
        ss.clear();
        value.push(st);
    }
    
    // 取数值堆栈前两个数,和运算符堆栈的运算符进行一次运算,运算结果仍放回数值堆栈
    void calcuOnce(stack<char>& op,stack<double>& value)
    {
        if(value.size()<2 || op.empty())
            return;
        double v1 = value.top();
        value.pop();
        double v2 = value.top();
        value.pop();
        char a = op.top();
        op.pop();
        double v3 = 0;
    
    //    cout << "to calculate once char " << c << ":" << v2 << a << v1 << endl;
    
        switch(a)
        {
            case '+':
                v3 = v2 + v1;
                break;
            case '-':
                v3 = v2 - v1;
                break;
            case '*':
                v3 = v2 * v1;
                break;
            case '/':
                v3 = v2 / v1;
                break;
            default:
                cout << "error:unknown op" << endl;
        }
        value.push(v3);
    }
    
    // 对输入的数值表达式进行计算
    double calculate(string inexp)
    {
        int i = 0;
        char c;
        stack<char> op;
        stack<double> value;    
        stringstream ss;
        double st = 0;
        for(i=0;i<inexp.length();i++)
        {
            c = inexp[i];
            // 如果当前字符是运算符
            if(c=='(' || c==')' || c=='+' || c=='-' || c=='*' || c=='/')
            {
                // 提取运算符前的数值
                if(!ss.eof())
                    getNum(ss,value);
                // 左括号,将(放入运算符堆栈
                if(c=='(' || op.empty())
                    op.push(c);
                // 右括号,进行计算,直到与左括号匹配相消
                else if(c==')')
                {
                    while(op.top()!='(')
                        calcuOnce(op,value);
                    op.pop();
                }
                // 一般运算符,比较与上一次运算符的优先级关系,如果上一次优先级高,完成上一次运算
                else if(cmpop(c,op.top()))
                {
                    calcuOnce(op,value);
                    op.push(c);
                }
                // 将运算符放入运算符堆栈
                else
                    op.push(c);
            }
            // 当前字符不是运算符,应该是数字或小数点
            else
            {
                ss << c;
                // 如果是处理最后一个字符,并且最后一个字符是数字,将最后一个数值提取出来,并完成剩余所有运算
                if(i==inexp.size()-1)
                {
                    getNum(ss,value);
                    while(!op.empty())
                        calcuOnce(op,value);
                }
            }
        }
        return value.top();
    }  
      
    int main()
    {
        description();
        while(1)
        {
            promt();
            string inexp;
            // 读取一行数值表达式
            getline(cin,inexp,'\n');
            // 如果没有输入,继续提示输入
            if(inexp.empty())
                continue;
            // 在输入q或者exit时推出程序
            else if(inexp=="q" || inexp=="exit")
                break;
            // 对输入的数值表达式进行计算
            else
            {
                double res = calculate(inexp);
                cout << "\t" << res << endl;
            }    
        }
        bye();
        return 0;
    }

    进行了一些测试,基本正确,也有可能有些地方有问题,希望大家指正。

    测试过程中有个问题不知道怎么解决,就是输入的时候,不能使用方向键,因为一般习惯输入一对括号,然后用方向键继续输入括号中的内容,在这里却没有办法使用方向键,只能使用退格键。希望那个大家指教。

  • 相关阅读:
    最小生成树 kruskal算法&prim算法
    Floyd算法解决多源最短路问题
    dijkstra算法解决单源最短路问题
    改进后的作业排序
    第一篇 基本结构
    循环轮转算法
    在线工具生成接入信息mqtt.fx快速接入阿里云
    NodeMCU使用ArduinoJson判断指定键值对存在与否
    NodeMCU获取并解析心知天气信息
    快速导出jekyll博客文件进行上传部署
  • 原文地址:https://www.cnblogs.com/Frandy/p/simple_command_line_calculator_v1.html
Copyright © 2011-2022 走看看