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

news2024/11/17 0:22:49

简单计算器

  • 一、项目介绍
  • 二、技术使用
  • 三、具体代码实现
    • 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。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SV-10A-4G IP网络报警非可视终端 (4G版)

SV-10A-4G IP网络报警非可视终端 &#xff08;4G版&#xff09; https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.621e3d0dpv5knb&ftt&id745728046948 产品简介&#xff1a; 通过局域网/广域网网组网的网络报警系统&#xff0c;改变传统局域网组网…

【MySql】10- 实践篇(八)

文章目录 1. 用动态的观点看加锁1.1 不等号条件里的等值查询1.2 等值查询的过程1.3 怎么看死锁&#xff1f;1.4 怎么看锁等待&#xff1f;1.5 update 的例子 2. 误删数据后怎么办?2.1 删除行2.2 误删库/表2.3 延迟复制备库2.4 预防误删库 / 表的方法2.4.1 账号分离2.4.2 制定操…

人工智能(8):Numpy的使用

1 Numpy介绍 Numpy&#xff08;Numerical Python&#xff09;是一个开源的Python科学计算库&#xff0c;用于快速处理任意维度的数组。 Numpy支持常见的数组和矩阵操作。对于同样的数值计算任务&#xff0c;使用Numpy比直接使用Python要简洁的多。 Numpy使用ndarray对象来处理…

NSGA-II 遗传多目标算法(python示例)

一、前言 最近在准备毕业论文&#xff0c;研究了一下主流的多目标算法&#xff0c;对于NSGA-II&#xff0c;网上大部分代码是全部是面向过程来实现的&#xff0c;本人更喜欢采用面向对象的方式&#xff0c;故采用python面向对象实现了一个示例&#xff0c;实现了对于二元多目标…

1400*C. Element Extermination(贪心规律)

Problem - 1375C - Codeforces 解析&#xff1a; 可以发现&#xff0c;最左端的数字&#xff0c;无论删除自己还是下一个&#xff0c;这个位置的值都不会变小。 同理&#xff0c;最右端位置的值都不会变大。 所以当最后剩余两个数字的时候&#xff0c;只有左端小于右端数字&…

STM32—PWM开发SG90舵机

目录 PWM介绍 PWM输出模式&#xff1a; ​编辑PWM占空比&#xff1a; PWM周期与频率公式&#xff1a;​编辑 SG90舵机介绍 1. 什么是舵机 2. 怎么控制舵机 SG90舵机介绍实战 1. 在 SYS 选项里&#xff0c;将 Debug 设为 Serial Wire​编辑 2. 将 RCC 里的 HSE 设置为 …

Vue的快速入门

Vue的快速入门 下载并安装vue.js Vue是一个基于JavaScript实现的框架, 要使用它就需要从[Vue官网]((https://cn.vuejs.org/)下载 vue.js 文件 第一步&#xff1a;打开Vue2官网&#xff0c;点击下图所示的“起步” 第二步&#xff1a;继续点击下图所示的“安装” 第三步&…

二叉树的遍历+二叉树的基本操作

文章目录 二叉树的操作一、 二叉树的存储1.二叉树的存储结构 二、 二叉树的基本操作1.前置创建一棵二叉树&#xff1a;1. 定义结点 2.简单的创建二叉树 2.二叉数的遍历1.前序遍历2.中序遍历3.后序遍历4.层序遍历 3.二叉树的操作1.获取树中节点的个数2.获取叶子节点的个数3.获取…

PHP的Excel导出与导入

下载地址&#xff08;注意php版本大于7.3可能会报错&#xff09; GitHub - PHPOffice/PHPExcel: ARCHIVED 解压 1、导出 Excel $data[[name>a,age>11],[name>b,age>22],[name>d,age>33], ]; $fileds["name">"名称","age"…

在Java和PostgreSQL枚举之间进行转换的通用方法

枚举类型&#xff08;enum&#xff09;是一种方便的数据类型&#xff0c;允许我们指定一个常量列表&#xff0c;对象字段或数据库列可以设置为该列表中的值。 枚举的美妙之处在于我们可以通过提供人类可读格式的枚举常量来确保数据完整性。因此&#xff0c;Java和PostgreSQL原…

MySQL 8.2 支持读写分离!

我们一直在等待的 MySQL 读/写分离功能 现在终于可以使用了&#xff01; 在规模上&#xff0c;我们在副本之间分配读取&#xff0c;但这必须在应用程序中以某种方式进行管理&#xff1a;指向在某个地方写入并在其他地方读取。 在 MySQL 8.2 中&#xff0c;MySQL Router 现在能…

SSH 无密登录设置

1 &#xff09; 配置 ssh &#xff08;1&#xff09;基本语法 ssh 另一台电脑的 IP 地址&#xff08;2&#xff09;ssh 连接时出现 Host key verification failed 的解决方法 [libaihadoop102 ~]$ ssh hadoop103 ➢ 如果出现如下内容 Are you sure you want to continue c…

设计模式(20)职责链模式

一、介绍&#xff1a; 1、定义&#xff1a;责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为设计模式&#xff0c;使多个对象都有机会处理请求&#xff0c;从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链&#xff0c;并沿着…

SpringSecurity6从入门到上天系列第二篇:搭建SpringSecurity6的入门级别程序!

文章目录 前言 1&#xff1a;环境要求 2&#xff1a;技术要求 一&#xff1a;搭建SpringBoot环境 1&#xff1a;创建空项目 2&#xff1a;创建SpringBoot项目 3&#xff1a;编写一个简单的controller 二&#xff1a;整合SpringSecurity 1&#xff1a;引入依赖 2&…

微信小程序设计之目录结构其他文件介绍

一、新建一个项目 首先&#xff0c;下载微信小程序开发工具&#xff0c;具体下载方式可以参考文章《微信小程序开发者工具下载》。 然后&#xff0c;注册小程序账号&#xff0c;具体注册方法&#xff0c;可以参考文章《微信小程序个人账号申请和配置详细教程》。 在得到了测…

Linux基础环境开发工具的使用(yum,vim,gcc,g++)

Linux基础环境开发工具的使用[yum,vim,gcc,g] 一.yum1.yum的快速入门1.yum安装软件2.yum卸载软件 2.yum的生态环境1.操作系统的分化2.四个问题1.服务器是谁提供的呢?2.服务器上的软件是谁提供的呢?3.为什么要提供呢?4.yum是如何得知目标服务器的地址和下载链接呢?5.软件源 …

XML教学视频(黑马程序员精讲 XML 知识!)笔记

第一章XML概述 1.1认识XML XML数据格式&#xff1a; 不是html但又和html有点相似 XML数据格式最主要的功能就是数据传输&#xff08;一个服务器到另一个服务器&#xff0c;一个网站到另一个网站&#xff09;配置文件、储存数据当做小型数据可使用、规范数据格式让数据具有结…

TypeScript深度剖析:TypeScript 中接口的应用场景?

一、是什么 接口是一系列抽象方法的声明&#xff0c;是一些方法特征的集合&#xff0c;这些方法都应该是抽象的&#xff0c;需要由具体的类去实现&#xff0c;然后第三方就可以通过这组抽象方法调用&#xff0c;让具体的类执行具体的方法 简单来讲&#xff0c;一个接口所描述…

PHP自定义文件缓存实现

文件缓存&#xff1a;可以将PHP脚本的执行结果缓存到文件中。当一个PHP脚本被请求时&#xff0c;先查看是否存在缓存文件&#xff0c;如果存在且未过期&#xff0c;则直接读取缓存文件内容返回给客户端&#xff0c;而无需执行脚本 1、文件缓存写法一&#xff0c;每个文件缓存一…

优化改进YOLOv5算法:加入SPD-Conv模块,让小目标无处遁形——(超详细)

1 SPD-Conv模块 论文:https://arxiv.org/pdf/2208.03641v1.pdf 摘要:卷积神经网络(CNNs)在计算即使觉任务中如图像分类和目标检测等取得了显著的成功。然而,当图像分辨率较低或物体较小时,它们的性能会灾难性下降。这是由于现有CNN常见的设计体系结构中有缺陷,即使用卷积…