1. 소개
웹 사이트에서 간단한 계산기를 만들 것인데 후위 표기법으로 작동하는 계산기를 만들어 볼 것이다.
2. 코드 및 구현
3. 구현 과정
const data = ["(", ")", "C", "%", "7", "8", "9", "÷", "4", "5", "6", "×", "1", "2", "3", "-", '.', "0", "=", "+"];
먼저 data라는 변수에 계산기 버튼의 요소들을 저장한다.
window.onload = function () {
for (let i = 0; i < data.length; i++)
create(i);
}
그리고 웹 페이지가 열릴 때 create라는 함수를 실행하는데 create는 버튼을 생성하는 함수이다.
/* 버튼 생성 */
function create(i) {
let btn = document.createElement("button");
btn.append(data[i]);
/* 연산자라면 */
if (isNaN(data[i]))
btn.className = "operator";
/* 피연산자 즉, 숫자라면 */
else
btn.className = "operand";
document.getElementById("container").appendChild(btn);
btn.addEventListener("click", function () {
if (data[i] == "C")
document.getElementById("input").value = "";
else if (data[i] == "=") {
let input = document.getElementById("input").value;
if (!input) //아무것도 입력을 하지 않을 경우
alert("Please fill in the blank");
else
result();
}
else
document.getElementById("input").value += data[i];
})
}
버튼의 태그를 생성하고 그곳에 data값을 저장한 다음에 data값에 따라 연산자인지 피 연산자인지 구분해준다. 그리고 C 버튼을 눌렀을 때는 input값을 전체 삭제하고 =을 눌렀을 때는 result 함수가 실행이 되는데 이 함수는 입력된 input 값을 후위 표기법으로 변환한 뒤 계산을 진행시켜준다. 그리고 나머지 버튼들을 입력했을 때는 input값에 해당하는 버튼의 요소들을 추가시켜준다.
function result() {
let input = document.getElementById("input").value;
input = input.replace(/(\s*)/g, ""); //공백문자 제거
let history = input + "="; //기록 용도
let stack = []; //연산자 임시 저장 및 후위 표기법 계산 결과 저장
let convert = []; //후위 표기법 저장
let temp = ""; //입력된 수 임시 저장
후위 표기법으로 변환하기 전에 필요한 변수들을 선언한다.
for (let i = 0; i < input.length; i++) {
let token = input[i];
if (!isNaN(token) || token == '.') {
temp += token;
if ((isNaN(input[i + 1]) || (i + 1 == input.length)) && input[i + 1] != '.') {
convert.push(temp);
temp = ""
}
}
intput의 값을 하나씩 token에 임시로 저장을 한 뒤에 token의 내용이 피연산자이거나 .이라면 temp 변수에 추가한다. 그리고 만약 input 값이 12.1+2이라고 한다면 아래 표와 같이 진행이 된다.
순번 | input | token | temp |
1 | 12.1+2 | 1 | 1 |
2 | 12.1+2 | 2 | 12 |
3 | 12.1+2 | . | 12. |
4 | 12.1+2 | 1 | 12.1 |
그리고 현재 위치에서 다음 input값이 연산자이거나 다음 input값이 없고, 다음 input값에 .이 아니라면 convert에 해당하는 temp를 저장한다. 그리고 temp를 초기화 시켜준다. 아래의 코드는 input값이 연산자일 경우이다.
else {
switch (token) {
case '(':
stack.push(token);
break;
case ')':
while (1) {
let popOP = stack.pop();
if (popOP == '(')
break;
convert.push(popOP);
}
break;
case '+':
case '-':
case '×':
case '*':
case '÷':
case '/':
case '%':
while (stack.length && WhoPrecOp(stack[stack.length - 1], token) >= 0) {
convert.push(stack.pop());
}
stack.push(token);
break;
}
}
}
(가 입력될 경우 바로 stack에 저장을 한다. 그리고 )가 입력이 되면 (가 나올 때까지 stack을 pop한다. 그 결과를 convert에 저장을 한다.
그리고 스택이 비어있지않고, 스택에 마지막 요소와 token 중 스택의 마지막 요소가 순위가 더 높다면 스택 요소를 convert에 저장하고 token값을 stack에 저장한다.
/* 연산자 순위 */
function GetOpPrec(op) {
switch (op) {
case '×':
case '*':
case '÷':
case '/':
case '%':
return 5;
case '+':
case '-':
return 3;
case '(':
return 1;
}
return -1;
}
/* 연산자 순위 비교 */
function WhoPrecOp(op1, op2) {
op1Prec = GetOpPrec(op1);
op2Prec = GetOpPrec(op2);
if (op1Prec > op2Prec)
return 1;
else if (op1Prec < op2Prec)
return -1;
else
return 0;
}
GetOpPrec은 연산자들 연산 순위를 매긴 것이고 WhoPrecOP는 연산 순위를 기반으로 연산 순위를 비교한 것이다.
while (stack.length != 0) {
convert.push(stack.pop());
}
그리고 마지막에 stack에 남은 요소들을 전부 convert에 저장한다.
for (let i = 0; i < convert.length; i++) {
let token = convert[i];
if (!isNaN(token)) {
stack.push(token);
}
그리고 convert의 요소에서 피연산자는 stack에 저장을 한다.
else {
let op2 = Number(stack.pop());
let op1 = Number(stack.pop());
if (!op1) op1 = 0;
switch (token) {
case '+':
stack.push(op1 + op2);
break;
case '-':
stack.push(op1 - op2);
break;
case '×':
case '*':
stack.push(op1 * op2);
break;
case '÷':
case '/':
stack.push(op1 / op2);
break;
case '%':
stack.push(op1 * op2 / 100);
break;
}
}
}
만약 연산자가 나오면 stack에 저장된 피연산자를 빼내서 해당 연산자에 맞게 계산을 진행한다. 그리고 연산 결과는 stack에 저장한다. 이것을 convert길이만큼 반복한다.
document.getElementById("input").value = stack;
history += stack;
record(history)
}
그리고 연산 결과가 저장된 stack값을 input 값으로 표기한다. 그리고 history에도 stack의 값을 추가하고 record 함수를 실행하는데 이 함수는 계산 결과 및 과정을 기록하는 함수이다.
/* 계산 과정 및 결과 기록 */
function record(result) {
let p = document.createElement("p");
p.append(result);
document.getElementById("record").prepend(p);
}
history값을 result로 가져와서 result에 p태그를 씌우고 기록이되어야하는 위치에 추가한다.
3. 느낀 점
윤성우의 열혈 자료구조 책을 보며 공부하는 도중에 스택을 이용한 후위 표기법에 대해 설명이 있는데 이것을 이용해서 비슷하게라도 웹으로 구현해보면 재미있을 것 같아서 구현해보았고, 추가적으로 소수점 계산을 할 수 있게 하였다. 다만 다중 괄호가 있는 연산을 불가능하고 간단한 연산들만 가능하다.
'컴퓨터 > 프로젝트' 카테고리의 다른 글
Calculator (0) | 2021.01.27 |
---|---|
TTS (text-to-speech)을 이용한 간단한 웹 (0) | 2021.01.11 |
무한 스크롤(Infinite Scrolling) (0) | 2021.01.01 |
To Do List (0) | 2020.12.25 |
To Do List(HTML, CSS, JavaScript) 만들기 (0) | 2020.12.25 |