1주일 가량 고민하여 만든 공학용 계산기.
다른 블로그 등에서는 eval로 계산하였으나, eval은...
eval 없이 공학용 계산기를 만들고자 하였고, 오류처리? 가 되지 않거나 사용자 편의성을 고려한 기능이 없음을 제외하면
계산을 올바르게 수행하는 것을 만들었다.
일반적인 계산기 코드들은 중위계산식을 사용하였다. 우리가 흔히 하는 식 : '3 * (2 + 1)'
중위 계산식은 개발자에게 직관적이지만 괄호 처리가 상당히 힘들다. 회사 사수 분도 괄호 처리로 몇 주를 앓으시다가 포기하셨다고...
이에 대한 대안이 후위 계산식이다. 위 식 '3 * (2 + 1)' >>> '3 * 2 1 + '
후위계산식은 computer 입장에서 알아보기 쉬운 방식이라 널리 쓰인다고 한다.
<후위 계산식 참조>
(1) https://wayhome25.github.io/cs/2017/04/18/cs-22/
(2) https://m.blog.naver.com/hack4ork/221671479879
(3) https://ywtechit.tistory.com/287
<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="1" class="num" style="height:100%; width:100%;"></td>
<td><input type="button" value="2" class="num" style="height:100%; width:100%;"></td>
<td><input type="button" value="3" class="num" style="height:100%; width:100%;"></td>
<td><input type="button" value="+" class="op" style="height:100%; width:100%;"></td>
<td><input type="button" value="tan" class="op" 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 = "";
// 연산자 우선 순위
function priority(operator) {
switch(operator) {
case '(': case ')':
return 0;
case '+': case '-':
return 1;
case '*': case '/':
return 2;
}
return 99;
}
// display 에 띄운 값을 받아 계산
let form = document.getElementById('display').value;
// 입력 값 공백 제거
form = form.replace(/(\s*)/g,"");
for(let i = 0; i < form.length; i++) {
const char = form.charAt(i);
const triLettersArr = 'sctp';
switch(char) {
case '(':
// 앞 문자가 삼각함수 연산자면 tmp 문자열에 더 함
if(triLettersArr.includes(form.charAt(i - 1)) && i !== 0) {
tmp += char;
}
else {
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 ')':
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)) {
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);
if(checkTriNum(form.charAt(i+1))) {
}
else {
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);
// 삼각함수 변환 로직
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); }
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'));
}
}
}
본인은 본래 객체지향을 선호하는 사람이라... 코드가 길어지니 너무나 슬펐다.
변수 하나, 함수 하나 변동에 따른 수정을 위에 드래그를 오가는 것도 너무 마음 아팠고...
그에따라 '모듈화' 기능을 통해 코드를 객체 분리하려 했으나, 너무 어려워서 중도 포기
나중에 기회가 된다면 '모듈화' 기능을 포함시키고 싶다.
'Project > JavaScript' 카테고리의 다른 글
[JavaScript 프로젝트 : 공학용 계산기] 공학용 계산기 upgraded (0) | 2022.01.26 |
---|---|
[JavaScript 프로젝트 : 공학용 계산기] 중복 연산자 제거 (0) | 2022.01.19 |
[JavaScript 프로젝트 : 공학용 계산기] 문자열 시작, 끝 검색 (0) | 2022.01.18 |
[JavaScript 프로젝트 : 공학용 계산기] 삼각함수 계산 (0) | 2022.01.18 |
댓글