이전 공학용 계산기의 기능이 사칙 연산 및 괄호에 따른 계산이었다.
이번 upgraded 버전은
(1) 연산자를 중복 입력하였을 시, 마지막 연산자만 기능되도록
(2) 삼각 함수 계산 추가 - 참고로 삼각함수는 sin( ) 등 반드시 괄호가 들어가도록 함
(3) 괄호가 잘못 입력시 오류 처리
(4) 소수점 기능
등을 추가 하였다.
업그레이드 과정의 70% 가량을 망할 괄호 처리에 소요하였으며, 너무 힘들었다...
왜 아무리 찾아도 괄호로 계산기 만든 사람이 없는지 이해되는...
<전체 코드 - HTML>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
<title>계산기</title>
<style>
table {
border-collapse: collapse;
}
td{
padding: 5px 10px;
text-align: center;
}
input{
border: none;
}
</style>
</head>
<body>
<script type="text/javascript" src="Clear.js"></script>
<script type="text/javascript" src="Sum.js"></script>
<h1>공학용 계산기</h1>
<form>
<table border="1">
<tr>
<td colspan="5"><input type="text" id="display" size="50" value="0"></td>
</tr>
<tr>
<td colspan="2">
<input type="button" value="clear" onclick="clearDisplay()" style="height:100%; width:100%;">
</td>
<td colspan="1">
<input type="button" value="(" onclick="add('(')" style="height:100%; width:100%;">
<td><input type="button" value=")" onclick="add(')')" style="height:100%; width:100%;"></td>
<td><input type="button" value="enter" onclick="sum()" style="height:100%; width:100%;"></td>
</td>
</tr>
<tr>
<td><input type="button" value="tan" onclick="add('t')" style="height:100%; width:100%;"></td>
<td><input type="button" value="1" onclick="add('1')" style="height:100%; width:100%;"></td>
<td><input type="button" value="2" onclick="add('2')" style="height:100%; width:100%;"></td>
<td><input type="button" value="3" onclick="add('3')" style="height:100%; width:100%;"></td>
<td><input type="button" value="+" onclick="add('+')" style="height:100%; width:100%;"></td>
</tr>
<tr>
<td><input type="button" value="sin" onclick="add('s')" style="height:100%; width:100%;"></td>
<td><input type="button" value="4" onclick="add('4')" style="height:100%; width:100%;"></td>
<td><input type="button" value="5" onclick="add('5')" style="height:100%; width:100%;"></td>
<td><input type="button" value="6" onclick="add('6')" style="height:100%; width:100%;"></td>
<td><input type="button" value="-" onclick="add('-')" style="height:100%; width:100%;"></td>
</tr>
<tr>
<td><input type="button" value="cos" onclick="add('c')" style="height:100%; width:100%;"></td>
<td><input type="button" value="7" onclick="add('7')" style="height:100%; width:100%;"></td>
<td><input type="button" value="8" onclick="add('8')" style="height:100%; width:100%;"></td>
<td><input type="button" value="9" onclick="add('9')" style="height:100%; width:100%;"></td>
<td><input type="button" value="*" onclick="add('*')" style="height:100%; width:100%;"></td>
</tr>
<tr>
<td><input type="button" value="x^y" onclick="add('p')" style="height:100%; width:100%;"></td>
<td colspan="2"><input type="button" value="0" onclick="add('0')" style="height:100%; width:100%;"></td>
<td><input type="button" value="/" onclick="add('/')" style="height:100%; width:100%;"></td>
<td><input type="button" value="." onclick="add('.')" style="height:100%; width:100%;"></td>
</tr>
</table>
</form>
</body>
</html>
<전체 코드 - JavaScript>
// 입력값 변수 input 선언 및 초기화
let input = "";
function add(letter){
// 입력 문자를 더하여
input = input + letter;
// display 창에 띄움
document.getElementById('display').value = input;
}
function sum() {
let stack = [];
let arr = [];
let tmp = "";
let del = 0;
// 연산자 우선 순위
function priority(operator) {
switch(operator) {
case '(': case ')':
return 0;
case '+': case '-':
return 1;
case '*': case '/':
return 2;
}
return 99;
}
// display 에 띄운 값을 받아 계산
const formOrigin = document.getElementById('display').value;
// 원본 문자열 변경 방지를 위한 새로운 문자열
let formCpy = formOrigin;
// 문자열 공백 제거
let formTrim = formCpy.replace(/(\s*)/g,"");
// 문자열 내 중복 연산자 제거
let formNoDup = removeDup(formTrim);
// 괄호에 대한 연산 오류 처리
let form = exception(formNoDup);
// pre-calculating of 삼각 함수 괄호 내부
for(let i = 0; i < form.length; i++) {
const char = form.charAt(i);
switch(char) {
case '(':
stack.push(char);
break;
case '+': case '-': case '*': case '/':
while(stack[stack.length - 1] != null &&
(priority(char) <= priority(stack[stack.length - 1]))) {
tmp += stack.pop();
if(isNaN(stack[stack.length - 1])) {
arr.push(tmp);
tmp = "";
}
}
stack.push(char);
break;
case ')':
if(del === 0) { }
else {
let returnOp = stack.pop();
while(returnOp != '(') {
tmp += returnOp;
returnOp = stack.pop();
if(isNaN(stack[stack.length - 1])) {
arr.push(tmp);
tmp = "";
}
}
break;
}
default:
tmp += char;
function checkTriNum(letters){
if(letters.includes('s')) return true;
else if(letters.includes('c')) return true;
else if(letters.includes('t')) return true;
else if(letters.includes('p')) return true;
}
// 삼각함수 안에 괄호가 있는건 여기서 고려하지 않고,
// 삼각함수 연산자 다음에 숫자가 나온 뒤 다른 연산자 혹은 문자열의 끝을 만나면
// 바로 삼각함수가 계산되도록 함.
if(isNaN(form.charAt(i+1)) || (i+1 === form.length)) {
// 삼각함수 검사
if(checkTriNum(tmp) && form.charAt(i+1) !== '(') {
console.log('tmp 진입 확인 : ' + tmp);
if(tmp.includes('s')) { tmp = triCal(tmp, 's'); }
else if(tmp.includes('c')) { tmp = triCal(tmp, 'c'); }
else if(tmp.includes('t')) { tmp = triCal(tmp, 't'); }
else if(tmp.includes('p')) { tmp = triCal(tmp, 'p'); }
}
console.log('triCal 계산값 : ' + tmp);
// 다음 연산자가 삼각함수, 소수점, '(' 면 tmp에 저장하고 skip
if(checkTriNum(form.charAt(i+1))) { }
else if(form.charAt(i+1) === '.') { }
else if(form.charAt(i+1) === '(') { }
else {
// del : 삼각함수 문자열에 든 ')' 가 다음 문자열에 포함되지 않게 함
if(form.charAt(i+1) === ')') { del++ }
console.log('tmp 전달 최종 : ' + tmp);
arr.push(tmp);
tmp = "";
}
}
break;
}
}
console.log('후위 연산식으로 들어갈 arr : ' + arr);
// stack 에 남은 연산자 to arr 이동
while(stack.length != 0) {
arr.push(stack.pop());
}
// 후위계산식
function solution(postFixArr) {
let stack = [];
for (let post of postFixArr) {
if(!isNaN(post)) stack.push(+post);
else {
let rightNum = stack.pop();
let leftNum = stack.pop();
if (post === '+') stack.push(leftNum + rightNum);
else if (post === '-') stack.push(leftNum - rightNum);
else if (post === '*') stack.push(leftNum * rightNum);
else if (post === '/') stack.push(leftNum / rightNum);
}
}
return +stack;
}
let result = "";
for(let element of arr ){
result += element;
result += " ";
}
console.log(form);
console.log(result);
// final result number
console.log("Result : " + solution(arr));
// html에 결과값 전달
document.getElementById('display').value = solution(arr);
// 괄호에 대한 오류 처리 code
function exception(errChk) {
const phrase = errChk;
// const alarm = '괄호 잘못 입력'
let i = 0;
let leftCnt = 0;
let rightCnt = 0;
let phraseArr = phrase.split("");
while(i !== phrase.length) {
// if 조건문이 각 2개이며, 가독성을 위해 각 부분에 중괄호 추가
{
if(phraseArr[i] === ('(')) { leftCnt++; }
else if(phraseArr[i] === (')')) { rightCnt++; }
i++;
}
{
// error : ')' 에 연속하여 숫자
if(phraseArr[i-1] === ')' && !isNaN(phraseArr[i])) {
return 0;
}
// error : 숫자에 연속하여 '('
else if(phraseArr[i] === '(' && !isNaN(phraseArr[i-1])) {
return 0;
}
}
}
// error : 괄호 갯수가 홀수
if(leftCnt !== rightCnt) {
return 0;
}
else {
return errChk;
}
}
// 연산자 중복 제거
function removeDup(phrase) {
let formPh = phrase;
let tmpArr = [];
for(let i=0; i < formPh.length; i++) {
let classifi = formPh.charAt(i);
// i-1 index 를 쓰기 위해 index[0] 은 항상 push
if(i === 0 ) {
tmpArr.push(classifi);
}
else {
// 숫자면
if(!isNaN(classifi)) {
tmpArr.push(classifi);
}
// 이전 및 현재 index 모두 연산자면
else if(isNaN(classifi) && isNaN(formPh.charAt(i-1))) {
// 이전 index ['(']
if(formPh.charAt(i-1) === '(') {
// 현재 index : ['('] >>> 이전 괄호 pop
if(classifi === '(') {
tmpArr.pop();
tmpArr.push(classifi);
}
// 현재 index : [')'] >>> 괄호 둘 다 제거
else if(classifi === ')') {
tmpArr.pop();
}
// 현재 index : ['연산자'] >>> 이전 중복 연산자 모두 제거. 현재 index 생존
else {
while(isNaN(tmpArr[tmpArr.length-1])) {
tmpArr.pop();
}
tmpArr.push(classifi);
}
}
// 이전 index [')']
else if(formPh.charAt(i-1) === ')') {
// 현재 index : ['('] || index : [')]
if(classifi === '(' || classifi === ')') { }
// 현재 index : ['나머지 연산자']
else {
tmpArr.push(classifi);
}
}
// 이전 index ['나머지 연산자']
else {
// 현재 index '('
if(classifi === '(') {
tmpArr.push(classifi);
}
// ')'
else if(classifi === ')') {
tmpArr.pop();
tmpArr.push(classifi);
}
// 'rest operator'
else {
tmpArr.pop();
tmpArr.push(classifi);
}
}
}
// 현재 index 는 연산자, 이전 index는 숫자
else if(isNaN(classifi) && !isNaN(formPh.charAt(i-1))) {
tmpArr.push(classifi);
}
}
}
let reformed = tmpArr.join("");
console.log(reformed);
return reformed;
}
// 삼각함수 변환 로직
function triCal(letters, triNum) {
console.log('afterTmp 전환 전 tmp 확인 : ' + letters);
// 삼각함수에 따라 split
let afterTmp = letters.split(/s|c|t|p/);
let num = '0';
console.log('afterTmp 변환 확인 : ' + afterTmp + '\n');
switch (triNum) {
case 's':
num = Math.sin(afterTmp[1]);
console.log(afterTmp[1]);
console.log('sin 계산 : ' + Math.sin(afterTmp[1]));
console.log('num 전환값 : ' + num);
if(!isNaN(parseFloat(afterTmp[0]))) {
return parseFloat(afterTmp[0]) * parseFloat(num);
}
else {
return parseFloat(num);
}
case 'c':
num = Math.cos(afterTmp[1]);
if(!isNaN(parseFloat(afterTmp[0]))) { return parseFloat(afterTmp[0]) * parseFloat(num); }
else { return parseFloat(num); }
case 't':
num = Math.tan(afterTmp[1]);
console.log('arr[1] : ' + afterTmp[1]);
console.log('num : ' + num + '\n');
console.log(afterTmp[1]);
console.log('tan 계산 : ' + Math.tan(afterTmp[1]));
console.log('num 전환값 : ' + num);
if(!isNaN(parseFloat(afterTmp[0]))) {
return parseFloat(afterTmp[0]) * parseFloat(num);
} else {
return parseFloat(num);
}
break;
case 'p':
if(!isNaN(parseFloat(afterTmp[0]))) {
num = Math.pow(parseFloat(afterTmp[0]), parseFloat(afterTmp[1]));
console.log('pow 계산값 : ' + num);
return parseFloat(num);
}
else return(console.log('입력 error'));
}
}
}
>>> FeedBack
사수 분 피셜 : hmtl 에서 onclick으로 문자열 add 하지 말고, html의 class 로 js에 직접 문자를 전달하면 좋다고 하셨다.
시간이 되면 추가 예정
'Project > JavaScript' 카테고리의 다른 글
[JavaScript] 웹 스크래핑(Web Scraping) 예제(2) (0) | 2022.07.06 |
---|---|
[JavaScript] 웹 스크래핑(Web Scraping) 예제(1) (0) | 2022.07.02 |
[JavaScript 프로젝트 : 공학용 계산기] 중복 연산자 제거 (0) | 2022.01.19 |
[JavaScript 프로젝트 : 공학용 계산기] 기본 공학용 계산기 (0) | 2022.01.19 |
댓글