由于课程js的需要 需要我们完成一个手敲的计算器 由于不准使用js中内置计算函数 所以只能自己敲 考虑算法 选择后缀表达式 也称为 逆波兰表达式
如果你不懂逆波兰表达式是什么没关系 举个例子
1.C-A*B = CAB*-
2.A*(B+C) -D = ABC+*D-
3.-A+B-C+D = A-B+C-D+
4.6+(8-2) * 2/3 = 682-2*3/+
其实你就可以观察到先运算的符号永远在后运算符号的后面 这也是我们实现其运算的关键思路
先提供计算器的基本样式代码
</html><!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="mian.css">
</head>
<body>
<div class="container">
<div class="calculator dark">
<label class="switch">
<input type="checkbox" id="theme-toggle">
<span class="slider"></span>
</label>
<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="/">÷</button></td>
<td><button class="btn-operator" id="*">×</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><button class="btn-number" id=".">.</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>
<td ><button class="btn-equal" id="equal">=</button></td>
</tr>
</table>
</div>
</div>
</div>
<script src="main.js">
</script>
</body>
</html>
接下来使CSS代码
*{
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: #272343;
}
.container111{
height: 100vh;
width: 100vw;
display: grid;
place-items: center;
margin: -250px auto;
margin-left: -150px;
}
.calculator{
position: relative;
margin:6% auto;
height: auto;
width: 23%;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 30px #000;
}
/* The switch - the box around the slider */
.switch {
font-size: 17px;
position: relative;
display: inline-block;
width: 3.5em;
height: 2em;
position: absolute;
top: 30px;
right: 30px;
color: #fff;
cursor: pointer;
z-index: 1;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
--background: #28096b;
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--background);
transition: .5s;
border-radius: 30px;
}
.slider:before {
position: absolute;
content: "";
height: 1.4em;
width: 1.4em;
border-radius: 50%;
left: 10%;
bottom: 15%;
box-shadow: inset 8px -4px 0px 0px #fff000;
background: var(--background);
transition: .5s;
}
input:checked + .slider {
background-color: #522ba7;
}
input:checked + .slider:before {
transform: translateX(100%);
box-shadow: inset 15px -4px 0px 15px #fff000;
}
.butbox button{
margin-top: 10px;
cursor: pointer;
border: none;
color: white;
background-color: rgb(112,101,254);
border-radius: 20px;
width: 100px;
height: 40px;
}
.butbox span{
margin-top: 10px;
cursor: pointer;
border: none;
color: white;
background-color: rgb(112,101,254);
border-radius: 20px;
width: 100px;
height: 40px;
}
#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);
}
/* 白天主题 */
.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: #ffd803;
color: #f967f3;
}
.calculator button.btn-equal{
background-color: #adf9e7;
color: #000;
}
/* 夜间主题 */
.calculator.dark{
background-color: #071115;
}
.calculator.dark #display{
color: #f8fafd;
}
.calculator.dark button#clear{
background-color: #2d191e;
color: #bd3740;
}
.calculator.dark button.btn-number{
background-color: #1b2f38;
color: #f8fafb;
}
.calculator.dark button.btn-operator{
background-color: #2e1f39;
color: #aa00a4;
}
.calculator.dark button.btn-equal{
background-color: #223323;
color: #fff;
}
现在说一下算法的思想
从左往右扫描逆波兰表达式 遇数字入栈;遇到运算符号,将栈中最上方的两个元素依次出栈并用运算符计算,将计算结果压入栈中。反复此过程
这样子讲可能有点抽象 举个例子
拿6+(8-2) * 2/3 = 682-2*3/+做实例
首先
682入栈 而后扫描到了-号 于是8-2为6 栈里就变成了66 而后继续扫描 到2 为662 扫描到了* 于是
6*2为12 栈就变成了6 12 依次类推
按步数来看
1. 6 8 2
2.6 6
3.6 12
4.6 4
5.10
这样子
最后栈里只剩下了10
接下来 详解一下我们js的关键函数
function fix(main) {
var output = [];
var stack = [];
for (var i = 0; i < main.length; i++) {
var taken = main[i];
if (!isNaN(taken)) {
var number = "";
while (!isNaN(taken) || taken === '.') {
number += taken;
i++;
taken = main[i];
}
output.push(number);
i--;
} else if (taken === '(') {
stack.push(taken);
} else if (taken === ")") {
while (stack.length > 0 && stack[stack.length - 1] !== '(') {
output.push(stack.pop());
}
stack.pop();
} else {
while (stack.length > 0 && fuhao(stack[stack.length - 1]) >= fuhao(taken)) {
output.push(stack.pop());
}
stack.push(taken);
}
}
while (stack.length > 0) {
output.push(stack.pop());
}
console.log(output);
return output;
}
主要就是这一段函数的作用 用来把我们的中缀表达式换成为后缀表达式
-
首先,定义两个数组变量 output 和 stack,output 数组用于存储转换后的后缀表达式,stack 数组用于存储运算符。
-
然后,通过 for 循环遍历输入的 main 中缀表达式数组中的每一个元素。
-
判断当前元素是否为数字,如果是,则表示为一个完整的数字,需要将连续的数字字符组合成一个完整的数字,并将其 push 到 output 数组中。使用 while 循环可以多次执行并将数字字符拼接成完整的数字。
-
如果当前元素是左括号 "(", 则将其直接 push 到 stack 数组中。
-
如果当前元素是右括号 ")", 则需要将 stack 数组中的运算符 pop 出来,直到遇到左括号 "(",并将这些运算符 push 到 output 数组中。注意,最后还需要将左括号 "(" 从 stack 数组中 pop 出来。
-
如果当前元素是运算符,则需要将 stack 数组中优先级比当前运算符高的运算符 pop 出来,并将这些运算符 push 到 output 数组中,然后将当前运算符 push 到 stack 数组中。
-
循环结束后,如果 stack 数组中还有运算符,则需要将这些运算符 pop 出来,并将其 push 到 output 数组中。
-
最后,返回 output 数组,即为转换后的后缀表达式。
如果还是有点没听懂没关系 接下来举个例子 希望能帮助你更好的去理解这段代码
给定中缀表达式:5*(54+56)/6
- 初始化 output = [] 和 stack = []
- 遍历中缀表达式 main:
- 第一个字符是 5,将其转换为字符串并添加到 output 中,output = ["5"]
- 下一个字符是 ,将其压入 stack 数组中,stack = [""]
- 下一个字符是 (,将其压入 stack 数组中,stack = ["*", "("]
- 下一个字符是 54,将其转换为字符串并添加到 output 中,output = ["5", "54"]
- 下一个字符是 +,其优先级大于栈顶运算符 * 的优先级,将其压入 stack 数组中,stack = ["*", "(", "+"]
- 下一个字符是 56,将其转换为字符串并添加到 output 中,output = ["5", "54", "56"]
- 下一个字符是 ),弹出栈顶元素 + 并添加到 output 中,output = ["5", "54", "56", "+"] 继续弹出栈顶元素 (,将后面的运算符添加到 output 中,output = ["5", "54", "56", "+", "*"]
- 下一个字符是 /,其优先级小于栈顶运算符 * 的优先级,将 * 弹出并添加到 output 中,output = ["5", "54", "56", "+", "*"] 将 / 压入 stack 数组中,stack = ["/"]
- 下一个字符是 6,将其转换为字符串并添加到 output 中,output = ["5", "54", "56", "+", "*", "6"]
- 遍历完中缀表达式 main 后,stack 数组中还剩下一个运算符 /,将其弹出并添加到 output 数组中,output = ["5", "54", "56", "+", "*", "6", "/"]
- 最后,output 数组即为转换完成的后缀表达式,即 ["5", "54", "56", "+", "*", "6", "/"]
我们的大概运算就是这个样子 希望可以理解 接下来是全部的js代码
const display = document.querySelector('#display');
const buttons = document.querySelectorAll('button');
const themeToggleBtn = document.getElementById('theme-toggle');
const calculator = document.querySelector('.calculator');
let isDark = true;
themeToggleBtn.addEventListener('change', () => {
calculator.classList.toggle('dark');
isDark = !isDark;
});
function cum(){
var main = display.innerText;
var check = Ex(main);
document.querySelector('#display').innerText = check;
}
function Ex(main) {
var post = fix(main);
var stack = [];
for (var i = 0; i < post.length; i++) {
var taken = post[i];
if (!isNaN(taken)) {
stack.push(parseFloat(taken))
} else {
var shu2 = stack.pop();
var shu1 = stack.pop();
switch (taken) {
case '+':
stack.push(shu1 + shu2);
break;
case '-':
stack.push(shu1 - shu2);
break;
case '*':
stack.push(shu1 * shu2);
break;
case '/':
stack.push(shu1 / shu2);
break;
}
}
}
return stack.pop()
}
function fix(main) {
var output = [];
var stack = [];
for (var i = 0; i < main.length; i++) {
var taken = main[i];
if (!isNaN(taken)) {
var number = "";
while (!isNaN(taken) || taken === '.') {
number += taken;
i++;
taken = main[i];
}
output.push(number);
i--;
} else if (taken === '(') {
stack.push(taken);
} else if (taken === ")") {
while (stack.length > 0 && stack[stack.length - 1] !== '(') {
output.push(stack.pop());
}
stack.pop();
} else {
while (stack.length > 0 && fuhao(stack[stack.length - 1]) >= fuhao(taken)) {
output.push(stack.pop());
}
stack.push(taken);
}
}
while (stack.length > 0) {
output.push(stack.pop());
}
console.log(output);
return output;
}
function fuhao(opter){
switch(opter){
case'+':
case'-':
return 1;
case'*':
case'/':
return 2;
default:
return 0;
}
}
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'){
cum();
} else if (display.innerText == '' && item.id == 'equal'){
display.innerText = 'Empty!';
setTimeout(() => (display.innerText = ''), 2000);
} else {
display.innerText += item.id;
}
}
})