数据结构Demo——简单计算器

简单计算器

  • 一、项目介绍
  • 二、技术使用
  • 三、具体代码实现
    • 1.前端部分
    • 2.后端部分

一、项目介绍

本项目实现了一个通过网页访问的简单计算器,它可以对带括号的加减乘除表达式进行计算并将计算结果返回给用户,并且可以对用户输入的表达式进行合法性判断,以下是项目的界面展示:
在这里插入图片描述
使用者可以通过点击网页上的按钮来输入一个算数表达式,之后点击等于号便可以将结果展示在界面上,具体效果如下:
在这里插入图片描述

二、技术使用

在这个计算器中主要使用了前端html,css,JavaScript,后端spring boot以及数据结构中栈的使用方式与相关的算法。

  • 在前端中使用了html来对界面进行了整体的布局,然后使用了css来对界面效果做了美化,最后使用JavaScript来实现每个按钮的点击事件,并且通过Ajax将请求参数发给后台服务器,然后将后台处理的结果接收并处理,最后展示给用户。
  • 在后端中主要使用了spring boot框架来搭建一个简单的服务器,并对前端发来的请求进行处理,最后将处理的结果返回给前端。
  • 在处理表达式的时候主要使用了数据结构中有关栈的一些知识,通过对栈的使用来对表达式进行计算。

三、具体代码实现

1.前端部分

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>计算器</title>
    <link rel="stylesheet" href="./css/style.css">
</head>
<body>
    <div class="container">
        <div class="calculator dark">
            <div class="display-screen">
                <div id="display"></div>
            </div>
            <div class="buttons">
                <table>
                    <tr>
                        <td><button class="btn-operator" id="clear">C</button></td>
                        <td><button class="btn-operator" id="/">&divide;</button></td>
                        <td><button class="btn-operator" id="*">&times;</button></td>
                        <td><button class="btn-operator" id="backspace"><</button></td>
                    </tr>
                    <tr>
                        <td><button class="btn-number" id="7">7</button></td>
                        <td><button class="btn-number" id="8">8</button></td>
                        <td><button class="btn-number" id="9">9</button></td>
                        <td><button class="btn-operator" id="-">-</button></td>
                    </tr>
                    <tr>
                        <td><button class="btn-number" id="4">4</button></td>
                        <td><button class="btn-number" id="5">5</button></td>
                        <td><button class="btn-number" id="6">6</button></td>
                        <td><button class="btn-operator" id="+">+</button></td>
                    </tr>
                    <tr>
                        <td><button class="btn-number" id="1">1</button></td>
                        <td><button class="btn-number" id="2">2</button></td>
                        <td><button class="btn-number" id="3">3</button></td>
                        <td rowspan="2"><button class="btn-equal" id="equal" onclick="submit()">=</button></td>
                    </tr>
                    <tr>
                        <td><button class="btn-operator" id="(">(</button></td>
                        <td><button class="btn-number" id="0">0</button></td>
                        <td><button class="btn-operator" id=")">)</button></td>
                    </tr>
                </table>
            </div>
        </div>
    </div>
    <script src="./js/script.js"></script>
</body>
</html>

以上为html部分的代码,主要是对使用者的界面进行了整体布局,确定了各个按钮的位置与功能。

*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    outline: 0;
    transition: all 0.5s ease;
}
body{
    font-family: sans-serif;
}
a{
    text-decoration: none;
    color: #fff;
}
body{
    background-image: linear-gradient(to bottom right, rgb(10, 88, 232), rgb(41, 231, 225));
}
.container{
    height: 100vh;
    width: 100vw;
    display: grid;
    place-items: center;
}
.calculator{
    position: relative;
    height: auto;
    width: auto;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 0 30px #000;
}
#display{
    margin: 0 10px;
    height: 150px;
    width: auto;
    max-width: 270px;
    display: flex;
    align-items: flex-end;
    justify-content: flex-end;
    font-size: 30px;
    overflow-x: scroll;
}
#display::-webkit-scrollbar{
    display: block;
    height: 3px;
}
button{
    height: 60px;
    width: 60px;
    border: 0;
    border-radius: 30px;
    margin: 5px;
    font-size: 20px;
    cursor: pointer;
    transition: all 200ms ease;
}
button:hover{
    transform: scale(1.1);
}
button#equal{
    height: 130px;
}

.calculator{
    background-color: #fff;
}
.calculator #display{
    color: #0a1e23;
}
.calculator button#clear{
    background-color: #ffd5d8;
    color: #fc4552;
}
.calculator button.btn-number{
    background-color: #c3eaff;
    color: #000;
}
.calculator button.btn-operator{
    background-color: #7ed0b0;
    color: #f39408;
}
.calculator button.btn-equal{
    background-color: #adf9e7;
    color: #000;
}

以上是css部分的代码,主要对界面的颜色样式进行了美化。

const display = document.querySelector('#display');
const buttons = document.querySelectorAll('button');

const submit = function () {
    let subdata = null;
    // 定义表单对象
    const data = {}
    // 获取input框内内容
    const displayData = display.innerText
    data.display = displayData
    console.log(data);
    const req = fetch('http://localhost:8081/cal/c', {
        body: JSON.stringify(data),
        method: "POST",
        headers: {
            'Content-Type': 'application/json'
        }
    })
    req.then(res => res.text())
        .then(res => {
            subdata = JSON.parse(res);
            if (!subdata.status)
                display.innerText = '输入格式错误'
            else {
                console.log(subdata);
                display.innerText = subdata.result
            }
        })
        .catch(err => {
            console.log(err)
        })
}

buttons.forEach((item) => {
    item.onclick = () => {
        if (item.id == 'clear') {
            display.innerText = '';
        } else if (item.id == 'backspace') {
            let string = display.innerText.toString();
            display.innerText = string.substr(0, string.length - 1);
        } else if (display.innerText != '' && item.id == 'equal') {
            submit()
        } else if (display.innerText == '' && item.id == 'equal') {
            display.innerText = 'Empty!';
            setTimeout(() => (display.innerText = ''), 2000);
        } else {
            display.innerText += item.id;
        }
    }
})
const calculator = document.querySelector('.calculator');

以上是JavaScript部分的代码,主要负责按钮的点击事件、给后端发送请求以及对后端返回结果的处理。

2.后端部分

@RestController
@RequestMapping("/cal")
public class Controller {
    @Autowired
    private ServiceImpl service;

    @PostMapping("/c")
    public Res calcula(@RequestBody data data) {
        return service.calculate(data.getDisplay());
    }
}

@Service
public class ServiceImpl {
    public Res calculate(String text) {
        return new Res(Calculator.isValidExpression(text) ? 
                Calculator.calculateExpression(text) : null,
                Calculator.isValidExpression(text) ? 1 : 0);
    }
}

以上是后端给出的请求接口以及业务逻辑层的方法。

@Data
@AllArgsConstructor
public class Res {
    public Integer result;
    public int status;
}

public class data {
    private String display;

    public data() {
    }

    public String getDisplay() {
        return display;
    }

    public void setDisplay(String display) {
        this.display = display;
    }

    public data(String display) {
        this.display = display;
    }

    @Override
    public String toString() {
        return "data{" +
                "display='" + display + '\'' +
                '}';
    }
}

以上是给前端返回结果的包装类以及接收前端数据的实体类。

public class Calculator {
    public static int priority(int oper) {
        if (oper == '*' || oper == '/') {
            return 1;
        } else if (oper == '+' || oper == '-') {
            return 0;
        } else {
            return -1;
        }
    }

    public static boolean isOper(char val) {
        return val == '+' || val == '-' || val == '*' || val == '/';
    }

    public static int cal(int num1, int num2, int oper) {
        int res = 0;
        switch (oper) {
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                res = num2 / num1;
                break;
        }
        return res;
    }

    public static int calculateExpression(String expression) {
        Stack<Integer> numStack = new Stack<>();
        Stack<Character> operStack = new Stack<>();
        int index = 0;
        int num1 = 0;
        int num2 = 0;
        int oper = 0;
        int res = 0;
        char ch = ' ';
        String keepNum = "";
        while (true) {
            ch = expression.substring(index, index + 1).charAt(0);
            if (isOper(ch)) {
                if (!operStack.isEmpty()) {
                    if (priority(ch) <= priority(operStack.peek())) {
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        res = cal(num1, num2, oper);
                        numStack.push(res);
                        operStack.push(ch);
                    } else {
                        operStack.push(ch);
                    }
                } else {
                    operStack.push(ch);
                }
            } else if (ch == '(') {
                int endIndex = getEndBracketIndex(expression, index);
                String subExpression = expression.substring(index + 1, endIndex);
                int subRes = calculateExpression(subExpression);
                numStack.push(subRes);
                index = endIndex;
            } else {
                keepNum += ch;
                if (index == expression.length() - 1) {
                    numStack.push(Integer.parseInt(keepNum));
                    keepNum = "";
                } else {
                    if (isOper(expression.substring(index + 1, index + 2).charAt(0))) {
                        numStack.push(Integer.parseInt(keepNum));
                        keepNum = "";
                    }
                }
            }
            index++;
            if (index >= expression.length()) {
                break;
            }
        }
        while (!operStack.isEmpty()) {
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = cal(num1, num2, oper);
            numStack.push(res);
        }
        return numStack.pop();
    }

    private static int getEndBracketIndex(String expression, int startIndex) {
        Stack<Integer> stack = new Stack<>();
        for (int i = startIndex; i < expression.length(); i++) {
            char ch = expression.charAt(i);
            if (ch == '(') {
                stack.push(i);
            } else if (ch == ')') {
                stack.pop();
                if (stack.isEmpty()) {
                    return i;
                }
            }
        }
        return 0;
    }

    public static boolean isValidExpression(String expr) {
        // 去除空格
        expr = expr.replaceAll("\\s", "");
        // 使用栈保存左括号
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < expr.length(); i++) {
            char c = expr.charAt(i);
            if (isLeftParenthesis(c)) {
                stack.push(c);
            } else if (isRightParenthesis(c)) {
                if (stack.isEmpty()) {
                    return false;
                } else {
                    stack.pop();
                }
            } else if (isOperator(c)) {
                if (i == 0 || i == expr.length() - 1 || isOperator(expr.charAt(i - 1)) || isOperator(expr.charAt(i + 1))) {
                    return false;
                }
                if (c == '/' && (i == expr.length() - 2 || expr.charAt(i + 2) == '0')) {
                    return false;
                }
            } else if (!Character.isDigit(c)) {
                return false;
            }
        }
        return stack.isEmpty();
    }

    public static boolean isLeftParenthesis(char c) {
        return c == '(';
    }

    public static boolean isRightParenthesis(char c) {
        return c == ')';
    }

    public static boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/';
    }
}

以上是对表达式进行处理并计算结果的一个简单的计算器类,该类提供了以下几个方法:

  • priority(int oper):根据运算符的优先级,返回一个整数值,其中乘法和除法的优先级为1,加法和减法的优先级为0,其他情况返回-1。
  • isOper(char val):判断给定字符是否为运算符(+、-、*、/)。
  • cal(int num1, int num2, int oper):根据给定的两个数字和一个运算符,进行相应的计算并返回结果。运算符对应的计算包括加法、减法、乘法和除法。
  • calculateExpression(String expression):通过指定的数学表达式进行计算,并返回计算结果。该方法使用两个栈来实现计算过程。一个栈用于存储数字,另一个栈用于存储运算符。遍历表达式的字符,根据字符的类型进行相应的操作。如果是运算符,则根据运算符的优先级决定是否进行计算;如果是左括号,则寻找对应的右括号,并将括号内的子表达式进行递归计算;如果是数字,则将数字压入数字栈中。最后,将剩余的运算符依次进行计算,直到栈为空,返回最终的计算结果。
  • getEndBracketIndex(String expression, int startIndex):辅助方法,用于获取给定表达式中与指定左括号对应的右括号的位置。
  • isValidExpression(String expr):判断给定的表达式是否为有效的数学表达式。方法首先去除字符串中的空格,然后使用栈来检查表达式中的括号是否匹配以及运算符的使用是否正确。具体规则如下:左括号入栈,遇到右括号出栈,如果栈为空则表示括号不匹配;如果遇到运算符,则判断其前后是否有运算符,以及除法运算符是否除以0,如果不符合规则则表达式无效;如果遇到非数字和非运算符的字符,则表达式无效。最后,如果栈为空,则表示括号匹配,返回true,否则返回false。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/109891.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Maven第二章:Maven基本概念与生命周期

Maven第二章&#xff1a;Maven基本概念与生命周期 前言 本章主要内容&#xff0c;介绍Maven基本概念&#xff0c;包括maven坐标含义&#xff0c;命名规则&#xff0c;继承与聚合、了解与理解生命周期&#xff0c;如何通过Maven进行依赖和版本管理。 什么是Maven的坐标&#xf…

【第25例】IPD体系进阶:需求分析团队RAT

目录 简介 RAT CSDN学院相关内容推荐 作者简介 简介 RAT是英文Requirement Analysis Team英文首字母的简称,也即需求分析团队,每个产品线都需要设定对应的一个RAT的组织。 RAT主要负责产品领域内需求的分析活动,是RMT的支撑团队: 这个时候可以将RAT细化为PL-RAT团队,…

Ansible的安装和部署

目录 1.Ansible的安装 2.构建Ansible清单 直接书写受管主机名或ip 设定受管主机的组[组名称] 主机规格的范围化操作 指定其他清单文件 ansible命令指定清单的正则表达式 3.Ansible配置文件参数详解 配置文件的分类与优先级 常用配置参数 4.构建用户级Ansible操作环…

Mysql数据库 4.SQL语言 DQL数据查询语言 查询

DQL数据查询语言 从数据表中提取满足特定条件的记录 1.单表查询 2.多表查询 查询基础语法 select 关键字后指定要查询到的记录的哪些列 语法&#xff1a;select 列名&#xff08;字段名&#xff09;/某几列/全部列 from 表名 [具体条件]&#xff1b; select colnumName…

linux进程概念

文章目录 1、冯诺依曼体系结构2、操作系统(Operator System)2.1、概念2.2、设计OS的目的2.3、定位2.4、如何理解 "管理"2.5、总结 3、系统调用和库函数概念4、进程4.1、基本概念4.2、描述进程-PCB4.2.1、task_struct-PCB的一种4.2.2、task_ struct内容分类 4.3、组织…

【Linux】第六站:Centos系统如何安装软件?

文章目录 1.Linux安装软件的方式2.Linux的软件生态3. yum4. rzsz软件的安装与卸载5.yum如何知道去哪里下载软件&#xff1f; 1.Linux安装软件的方式 在linux中安装软件常用的有三种方式 源代码安装&#xff08;我们还需要进行编译运行后才可以&#xff0c;很麻烦&#xff09; …

H5游戏源码分享-跳得更高

H5游戏源码分享-跳得更高 控制跳动踩到云朵上 <!DOCTYPE html> <html> <head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><meta http-equiv"Content-Type" content"text/html;"&g…

基于SSM的养老院管理系统

基于SSM的养老院管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatisVUE工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 摘要 养老院管理系统是一个基于SSM&#xff08;Spring、Spring MVC、MyBatis&…

[量化投资-学习笔记002]Python+TDengine从零开始搭建量化分析平台-MA均线的多种实现方式

MA 均线时最基本的技术指标&#xff0c;也是最简单&#xff0c;最不常用的&#xff08;通常使用EMA、SMA&#xff09;。 以下用两种不同的计算方法和两种不同的画图方法进行展示和说明。 MA 均线指标公式 MA (N)(C1 C2 C3 …C N )/N目录 方式一1.SQL 直接查询均值2.使用 pyp…

java八股文(基础篇)

面向过程和面向对象的区别 面向过程&#xff1a;在解决问题时&#xff0c;特别自定义函数编写一步一步的步骤解决问题。 面向对象&#xff1a;其特点就是 继承&#xff0c;多态&#xff0c;继承&#xff0c;在解决问题时&#xff0c;不再注重函数的编写&#xff0c;而在于注重…

这么理解矩阵乘法,让你吊打面试官

大家好啊&#xff0c;我是董董灿。 很多与深度学习算法相关的面试&#xff0c;面试官可能都会问一个问题&#xff0c;那就是你是如何理解矩阵乘算法的。 更有甚者&#xff0c;会让你当场手写矩阵乘算法&#xff0c;然后问细节&#xff0c;问如何优化&#xff0c;面试现场&…

治疗红斑性肢痛症的【Chromocell】申请870万美元纳斯达克IPO上市

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;总部位于美国的生物制药公司Chromocell Therapeutics Corporation&#xff08;简称&#xff1a;Chromocell&#xff09;近期已向美国证券交易委员会&#xff08;SEC&#xff09;提交招股书&#x…

VM搭建虚拟机2(自定义安装)

文章目录 自定义安装选择你的centos下载目录设置用户名密码自定义安装目录注意&#xff0c;尽量别再同一位置安装虚拟机设置处理器数量内存根据所需配置&#xff08;默认1G&#xff09;NAT按需设置磁盘大小点击完成即可等待安装即可 VMware、centos、典型安装 自定义安装 选择你…

【机器学习(二) 线性代数基础I(Linear Algebra Foundations)】

机器学习&#xff08;二&#xff09; 线性代数基础I&#xff08;Linear Algebra Foundations) 这一节主要介绍一些线性代数的基础。 目录 机器学习&#xff08;二&#xff09; 线性代数基础I&#xff08;Linear Algebra Foundations)1. 向量 Vectors2. 复杂度 Complexity3.线…

【Linux】第七站:vim的使用以及配置

文章目录 一、vim1.vim的介绍2.vim基本使用3.vim的命令模式常用命令4.底行模式 二、vim的配置 一、vim 1.vim的介绍 vim编辑器&#xff0c;用来文本编写&#xff0c;可以写代码 它是一个多模式的编辑器 它有很多的模&#xff0c;不过我们暂时先只考虑这三种模式 命令模式插入模…

2023年阿里云双11有什么优惠活动?详细攻略来了!

随着双十一的临近&#xff0c;阿里云也正式开启了双11大促&#xff0c;推出了“金秋云创季”活动&#xff0c;那么&#xff0c;2023年阿里云双11的优惠活动究竟有哪些呢&#xff1f;本文将为大家详细介绍。 一、阿里云双11活动时间 1、2023年10月27日-2023年10月31日&#xff…

洛谷 B2009 计算 (a+b)/c 的值 C++代码

目录 题目描述 AC Code 切记 题目描述 题目网址&#xff1a;计算 (ab)/c 的值 - 洛谷 AC Code #include<bits/stdc.h> using namespace std; int main() {int a,b,c;cin>>a>>b>>c;cout<<(ab)/c<<endl;return 0; } 切记 不要复制题…

[论文阅读]Ghost-free High Dynamic Range Imaging with Context-aware Transformer

Ghost-free HDRI with Context-aware Transformer 背景介绍已有算法本文算法实验对比 背景介绍 高动态范围成像&#xff08;HDR&#xff09;是一种图像技术&#xff0c;它能够捕捉到比传统图像更广泛的亮度范围。1997年&#xff0c;Paul Debevec在他的论文《Recovering High D…

Netty复习:(2)IdleStateHandler的用法

一、handler定义&#xff1a; package handler;import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter;public class MyChatServerHandler3 extends ChannelInboundHandlerAdapter {Overridepublic void userEventTriggered(…

第N个斐波那契数列

第N个斐波那契数列 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 class Solution { public:int tribonacci(int n) {int a[4]{0,1,1,2};if(n<4) return a[n];int kn-3;for(int i0; i<k;i){int tmpa[3];a[3]a[1]a[2]a[3];//不是【0】开始&…