본문 바로가기
Project/JavaScript

[JavaScript 프로젝트 : 공학용 계산기] 기본 공학용 계산기

by 꾸압 2022. 1. 19.

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/

 

강의노트 21. 자료구조 - stack을 이용한 후위표기법 계산기 만들기 · 초보몽키의 개발공부로그

패스트캠퍼스 컴퓨터공학 입문 수업을 듣고 중요한 내용을 정리했습니다. 개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.

wayhome25.github.io

(2) https://m.blog.naver.com/hack4ork/221671479879

 

공학용 계산기 (후위 연산 알고리즘)

[업데이트 2021.02.13] ====================================================== 1. 해결 가능 언어 - C,...

blog.naver.com

(3) https://ywtechit.tistory.com/287

 

[ 자바스크립트(JavaScript) ] section06 - 4 - 후위식 연산(postfix)

📍 section06 - 4 - 후위식 연산(postfix) 간단하게 후위식 연산에 대해서 알아보자면, 우리가 일반적으로 사용하는 사칙연산 7+7 은 중위표기식이라고 한다. (숫자 사이에 +-*/ 가 들어감.) 후위표기식

ywtechit.tistory.com

 

 

<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'));
        }
    }
}

 

본인은 본래 객체지향을 선호하는 사람이라... 코드가 길어지니 너무나 슬펐다.

변수 하나, 함수 하나 변동에 따른 수정을 위에 드래그를 오가는 것도 너무 마음 아팠고...

 

그에따라 '모듈화' 기능을 통해 코드를 객체 분리하려 했으나, 너무 어려워서 중도 포기

나중에 기회가 된다면 '모듈화' 기능을 포함시키고 싶다.

 

 

댓글