计算器核心解析算法(上)

计算机如何读懂四则运算表达式?
9.3 + (3 - -0.11) * 5

后缀表达式
人类习惯的数学表达式叫做中缀表达式
另外,还有一种将运算符放在数字后面的后缀表达式
5 + 3——> 5 3 +
1 + 2 * 3 ——> 1 2 3 * +
9 + (3 - 1) *5 ——> 9 3 1 - 5* +

中缀表达式符合人类的阅读和思维习惯
后缀表达式符合计算机的运算方式
——消除了中缀表达式中的括号
——同时保留中缀表达式中的运算优先级

解决方案
1.将中缀表达式进行数字和运算符的分离
2.将中缀表达式转换为后缀表达式
3.通过后缀表达式计算最终结果

分离算法分析
所要计算的中缀表达式中包含
——数字和小数点[ 0-9或.]
——符号位[+ 或-]
——运算符[+ - * /]
——括号 [( 或 )]

9.3 + ( 3 - -0.11 ) * 5

思想:以符号作为标志对表达式中的字符逐个访问
——定义累计变量num(字符串)
——当前字符exp[i]为数字或小数点时:
    累计:num += exp[i]
——当前字符exp[i]为符号时:
   num为运算数,分离并保存
    若exp[i]为正负号:
      累计符号位 +和- : num +=exp[i]
    若exp[i]为运算符:
      分离并保存

for(int i=0; i<exp.length(); i++)
{
    if( exp[i]为数字或小数点)
        累计:num += exp[i];
    else if(exp[i]为符号)
    {
        if( num != "")
            分离并保存运算数: num
        if(exp[i]为正号或负号)
            符号位累计: num += exp[i]
        else
        {
            分离并保存运算符: exp[i];
        }
    }
}

难点:
——如何区分正负号与加号和减号
+ 和 - 在表达式的第一个位置    (前一个字符为空,必然是正负号)
括号后的 + 和 -                        (前一个字符是括号,必然是正负号)
运算符后的 + 和 -                     (前一个字符是运算符,必然是正负号)

QCalculatorDec.h

#ifndef _QCALCULATORDEC_H_
#define _QCALCULATORDEC_H_

#include <QString>
#include <QQueue>
#include <QStack>

class QCalculatorDec
{
protected:
    QString m_exp;  // 代表用户输入的四则运算表达式
    QString m_result; //计算结果

    bool isDigitOrDot(QChar c);
    bool isSymbol(QChar c);
    bool isSign(QChar c);
    bool isNumber(QString s);
    bool isOperator(QString s);
    bool isLeft(QString s);
    bool isRight(QString s);
    int priority(QString s);

    QQueue<QString> split(const QString& exp);

public:
    QCalculatorDec();
    ~QCalculatorDec();
    bool expression(const QString& exp);
    QString expression();
    QString result();
};

#endif // _QCALCULATORDEC_H_

QCalculatorDec.cpp

#include "QCalculatorDec.h"
#include <QDebug>

QCalculatorDec::QCalculatorDec()
{
    m_exp = " ";
    m_result = " ";
//为了测试使用
    QQueue<QString> r = split("-9.11 + (3 - -1)* -5");

    for(int i=0; i<r.length(); i++)
    {
        qDebug() << r[i];
    }
}

QCalculatorDec::~QCalculatorDec()
{

}

bool QCalculatorDec::isDigitOrDot(QChar c)
{
    return (((‘0‘ <= c) && (c <= ‘9‘)) || (c == ‘.‘));
}

bool QCalculatorDec::isSymbol(QChar c)   //判读当前的字符C究竟是不是操作符或者括号
{
    return isOperator(c) || (c == ‘(‘) || (c == ‘)‘);
}

bool QCalculatorDec::isSign(QChar c)  //判断当前的字符是不是正负号
{
    return (c == ‘+‘) || (c == ‘-‘);
}

bool QCalculatorDec::isNumber(QString s)  //判断当前的s是不是合法的数字
{
    bool ret = false;

    s.toDouble(&ret);

    return ret;
}

bool QCalculatorDec::isOperator(QString s)
{
    return (s == "+") || (s == "-") || (s == "*") || (s == "/") ;
}

bool QCalculatorDec::isLeft(QString s)
{
    return (s == "(");
}

bool QCalculatorDec::isRight(QString s)
{
    return (s == ")");
}

int QCalculatorDec::priority(QString s)
{
    int ret = 0;

    if((s == "+") || (s == "-"))
    {
        ret = 1;
    }

    if((s == "*") || (s == "/"))
    {
        ret = 2;
    }

    return ret;
}

bool QCalculatorDec::expression(const QString &exp)
{
    bool ret = false;

    return ret;
}

QString QCalculatorDec::result()
{
    return m_result;
}

QQueue<QString> QCalculatorDec::split(const QString &exp)
{
    QQueue<QString> ret;
    QString num = "";
    QString pre = ""; //用来保存前一个字符的

    for(int i=0; i<exp.length(); i++)
    {
        if(isDigitOrDot(exp[i]))
        {
            num += exp[i];
            pre = exp[i];
        }
        else if(isSymbol(exp[i]))
        {
            if(!num.isEmpty())
            {
                ret.enqueue(num);   //如果不为空,就应该分离并保存了。保存到队列中,之后num就应该清空,以便累计下一个运算数。

                num.clear();
            }

            if(isSign(exp[i]) && ((pre == "") || (pre == "(") || isOperator(pre)))
            {
                num += exp[i];
            }
            else
            {
                ret.enqueue(exp[i]);
            }

            pre = exp[i];//将这个字符保存下来,当进行下一次循环时,它将作为前一个字符使用
        }
    }

    if(!num.isEmpty())  //如果for循环运行结束之后,num变量里面还有没有东西呢?如果不为空,里面还保存着最后的一个运算数。应将其分离保存到返回队列中去。
    {
        ret.enqueue(num);
        qDebug() << num;
    }   //这个地方很关键,四则运算中的最后一个操作数。

    return ret;
}

main.cpp

#include <QApplication>
#include "QCalculatorUI.h"
#include "QCalculatorDec.h"

int main(int argc, char *argv[])
{
#if 0
    QApplication a(argc, argv);
    QCalculatorUI* cal = QCalculatorUI::NewInstance();
    int ret = 0;

    if(cal != NULL)
    {
        cal->show();
        ret = a.exec();
        delete cal; //当程序运行到最后时,将生成的cal对象释放掉。
    }

    return ret;
#endif

    QCalculatorDec c;

    return 0;

}

原文地址:https://www.cnblogs.com/-glb/p/12094213.html

时间: 2024-08-10 16:13:33

计算器核心解析算法(上)的相关文章

第14课 计算器核心解析算法(下)

1. 中缀到后缀的手工转换 (1)按优先级给表达式加括号 (2)从内向外将运算符移动对应的括号后面 (3)去括号,即可得到后缀表达式(如下图,图中数字表示转换顺序) 2. 后缀表达式求值 2.1 图解后缀表达式求值过程 (1)从左向右扫描后缀表达式. (2)遇到数字直接进栈 (3)重点在于遇到运算符时,要找到其左右操作数.(此图中有5个运算符,分别是*./.-.*.+) 2.2 求值步骤(利用栈来进行运算,注意栈只用来保存操作数,并不保存操作符) (1)当前元素为数字:进栈 (2)当前元素为运算

QT之计算器核心解析算法

上节我们说到计算机默认的是后缀表达式,那么中缀表达式转后缀表达式的过程就类似于编译过程. 必须得注意这么几个问题:四则运算表达式中的括号必须匹配:根据运算符优先级进行转换:转换后的表达式中没有括号:转换后可以顺序计算出最终结果.下来我们就讲下具体的转换过程:1.当前元素 e 为数字:输出2.当前元素 e 为运算符时:1. 与栈顶运算符进行优先级比较:2.小于等于时将栈顶元素输出,转1:3.大于时将当前元素 e 入栈3.当前元素 e 为左括号:入栈4.当前元素 e 为右括号:1.弹出栈顶元素并输出

第十三课、计算器核心解析算法(中)

一.中缀转后缀 1.中缀表达式转后缀表达式的过程类似编译过程 (1).四则运算表达式中的括号必须匹配 (2).根据运算符优先级进行转换 (3).转换后的表达式中没有括号 (4).转换后可以顺序计算出最后结果 2.转换过程 (1).当元素e为数字:输出 (2).当元素e为运算符: A.与栈顶运算符进行优先级比较 B.小于等于:将栈顶元素输出,转A执行 C.大于:将当前元素e入栈 (3).当前元素为e为左括号:入栈 (4).当前元素e为右括号: A.弹出栈顶元素并输出,直至栈顶元素为左括号 B.将栈

AngularJS的核心对象angular上的方法全面解析(AngularJS全局API)

总结一下AngularJS的核心对象angular上的方法,也帮助自己学习一下平时工作中没怎么用到的方法,看能不能提高开发效率.我当前使用的Angularjs版本是1.5.5也是目前最新的稳定版本,不过在全局API上,版本不同也没什么区别. AngularJS 全局 API列表 element bootstrap copy extend merge equals forEach noop bind toJson fromJson identity isUndefined isDefined is

[转帖]2019年常见ElasticSearch 面试题解析(上)

2019年常见ElasticSearch 面试题解析(上) https://juejin.im/post/5e0348d8e51d45582512a59f 前言 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎.ElasticSearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安

详解AJAX核心 —— XMLHttpRequest 对象 (上)

我要说的内容都是非常基础的内容,高手就免看了,如果看了欢迎给点意见啊.新手或者对低层还不是很了解的人可以看看,帮助理解与记忆. XMLHttpRequest 对象是AJAX功能的核心,要开发AJAX程序必须从了解XMLHttpRequest 对象开始. 了解XMLHttpRequest 对象就先从创建XMLHttpRequest 对象开始,在不同的浏览器中创建XMLHttpRequest 对象使用不同的方法: 先看看IE创建XMLHttpRequest 对象的方法(方法1): var xmlht

multer 解析文件上传

Express默认并不处理HTTP请求体中的数据,对于普通请求体(JSON.二进制.字符串)数据,可以使用body-parser中间件.而文件上传(multipart/form-data请求),可以基于请求流处理,也可以使用formidable模块或Multer中间件. 1. multer中间件 Multer是Express官方推出的,用于Node.jsmultipart/form-data请求数据处理的中间件. 它基于busboy构建,可以高效的处理文件上传,但并不处理multipart/fo

Gson全解析(上)-Gson基础

前言 最近在研究Retrofit中使用的Gson的时候,发现对Gson的一些深层次的概念和使用比较模糊,所以这里做一个知识点的归纳整理. Gson(又称Google Gson)是Google公司发布的一个开放源代码的Java库,主要用途为序列化Java对象为JSON字符串,或反序列化JSON字符串成Java对象.而JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成,广泛应用于各种数据的交互中,尤其是服务器与客户

一步一步写算法(之prim算法 上)

原文:一步一步写算法(之prim算法 上) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 前面我们讨论了图的创建.添加.删除和保存等问题.今天我们将继续讨论图的一些其他问题,比如说如何在图的环境下构建最小生成树.为什么要构建最小生成树呢?其实原理很简单.打个比方,现在某一个乡镇有n个村,那么这n个村肯定是联通的.现在我们打算在各个村之间搭建网线,实现村村通的工程.那么有什么办法可以实现村村互通,同时又使得最后的总距离最小呢?要达到这个目