공학용 계산기 만들기 - boostcamp-2020/Project15-C-Client-Based-Formula-Editor GitHub Wiki
-
- calculator 부분부터 보셔도 될 것 같습니다. 보다보면 재미있습니다.
- https://www.youtube.com/watch?v=52GL_cfLBYs&t=3840s
Feature
- ANS : 이전 결과값 이용 버튼
- C : clear
- remove : 한 글자씩 지우기
- Rad/Deg : 라디안 단위 , 각도 단위
- Rad : cos(pie) = -1
- Deg : cos(180) = -1
- power : 제곱, Math.pow(base, exponent)
버튼
01. 형태
- 각 버튼들은 객체형태이다.
{
name: rad, button element 생성시 id 값으로 들어감
symbol: Rad, button에 보여질 내용 및 operation에 보여질 내용
formula: false, 백그라운드에서 실제 계산될 때 사용될 내용 (JS 코드)
type: key, button의 타입을 분류
}
- formula는 false or empty string or nothing
- type은 key, math_function, number, trigo_function, operator, calculate(=)
- trigonometric : 삼각법, inverse trigonometric : 역삼각법
02. 이벤트
- 부모 div 붙인 하나의 이벤트 리스너로 동작
- event.target으로부터 id값을 읽어와서 caclulator_buttons 라는 객체 배열에서 반복문을 통해서 객체를 찾는다.
- 객체(
{name, symbol, formula, type}
)를 찾았으면 그 객체를 calculator 함수의 매개변수로 전달한다.
03. calculator 함수
- type은 총 6개
- number, operator, math_function, trigo_function, key, calculate
- number → operationArray.push(symbol), formulaArray.push(formula)
- operator, math_function, trigo_function(little different) → number와 동일
- key → Rad, Deg, C, Remove
- operationArray = [4, 5, "-", "cos(", 5, 0, ")", "+", "pie"] (symbol들 저장)
- operationArray.join('') = "45-cos(50)+pie" (화면에 츨력)
- formulaArray = [4, 5, "-", "Math.cos(", 5, 0, ")", "+", "Math.PI"] (formula들 저장)
- formulaArray.join('') = "45-Math.cos(50)+Math.pI" (eval 함수를 호출한다.)
The eval() function evaluates JavaScript code represented as a string eval 함수는 문자열로 표현된 JS 코드를 계산하는 함수이다. ex. eval("alert('key')"); ⇒ 알림창을 띄운다.
- "45-Math.cos(50)+Math.pI" 문자열을 eval 함수를 실행해 JS 코드로 계산한다. 그리고 (화면에 출력)
- operationArray와 formulaArray는 항상 동기화되어야 한다.
04. calculator 함수 : math_function (factorial)
- Math.pow는 built-in function이지만 factorial은 만들어야 한다.
function factorial(num){
if(num % 1 !== 0) return gamma(num + 1); // +1은 gamma func의 사용법
// 정수는 1로 나눈 나머지가 0이지만 소수는 1로 나눈 나머지가 0이 아니니까
// 이 방식으로 소수를 검출하고 소수는 gamma func으로 해결한다.
if(num === 0 || num === 1) return 1;
let result = 1;
for(let i=1; i<=num; i++){
result = result * i;
if(resuslt === Infinity) return Infinity;
}
return result;
}
- 1/2! 같은 경우는 정수와 같은 방식으로 계산할 수 없고, gamma function을 호출해야 한다. 너무 복잡해서 stack overflow를 참고해서 사용한다.
- https://stackoverflow.com/questions/15454183/how-to-make-a-function-that-computes-the-factorial-for-numbers-with-decimals#answer-15454866
05. Fix : factorial, power
- Factorial
- factorial을 찾고, 있으면 factorial 앞의 숫자를 가져온다. factorialNumberGetter는 2를 return 할 것이다. 그리고 모양이
factorial(2)
로 바뀌어야 한다. - formula_str에서 replace 함수를 호출한다. 이때 factorial 앞의 모든 숫자, toReplace, repacement 이렇게 3가지가 필요하다.
- Power
- factorial과 마찬가지 power 앞의 숫자를 가져온다. powerBaseGetter는 4를 return 할 것이다.
- 위 두함수를 통해 아래의 결과를 만들어낼 것이다.
06. Fix : Power
// data.formula === formulaArray
const POWER = "POWER(";
let POWER_SEARCH_RESULT = search(formulaArray, POWER); // [3, 17];
function search(array, keyword) {
let result_array = [];
array.forEach(element, index) => {
if(keyword === element) result_array.push(index);
});
return result_array;
}
- powerBaseGetter
- 모든 bases를 찾아서 리턴한다. (powers_bases 배열에 담아서)
- power index의 바로 앞(-1)부터가 base의 마지막 index이다. 거기서 시작해서 맨 앞까지 가면서 base를 찾는다. (index를 1씩 줄여가면서)
- formula 는 formulaArray와 동일하게 봐도 된다. 사용자가 입력한 모든 formula가 들어있는 배열
- push 말고 unshift를 쓴다. 이유는 뒤에서부터 앞으로 탐색을 하기 때문이다.
- unshift() 메서드는 새로운 요소를 배열의 맨 앞쪽에 추가하고, 새로운 길이를 반환합니다.
- 앞방향으로 탐색을 해나가다가 operator일 경우 break 한다.
- parentheses(괄호)를 세다가 여는 괄호와 닫는 괄호가 같을 경우 break 한다. (위 조건과 묶는다.)
- 5 POWER( 6 POWER 2 ) ) 인 경우도 있다. 그래서 power( 인 경우에도 break를 한다.
- base를 ('')로 join하고 powers_bases에 담는다.
- replace
- powerBaseGetter 함수를 통해 얻은 bases를 이용해서 JS 형식에 맞게 replace 해준다.
07 Fix : Factorial
// data.formula === formulaArray
cosnt FACTORIAL = "FACTORIAL:
let FACTORIAL_SEARCH_RESULT = search(formulaArray, FACTORIAL);
// [3, 8, 9, 10]
- factorialNumberGetter
- powerBaseGetter랑은 다르게 이전 index가 아닌 다음 index를 먼저 검사한다. 다음 index가 factorial인지 검사를 진행한다.
- index 8을 먼저 검사해보면 다음 index가 factorial이다. 이럴 떄는 factorial_sequence를 증가시키고 for loop를 끝낸다. index 9으로 가서 똑같이 진행하고 index 10으로 간다.
- index 10은 마지막 factorial이므로 연속된 factorial 중에서 가장 처음 factorial의 index 값을 구한다. (factorial_sequence를 빼는 방법을 통해서)
- 그리고 가장 처음 factorial의 index에 -1을 한 값을 가지고 다시 반복문을 돈다.
- powerBaseGetter 처럼 한 개씩 담아가면서 맨 앞 index로 이동한다.
- 마찬가지로 앞으로 탐색하다가 operator일 경우 break;
- 여는 괄호와 닫는 괄호가 같은 경우 break;
- 이렇게 factorial의 모든 number를 구했으면 join한다.
- factorial_sequence에 + 1 을 해서 factorial 의 반복 개수를 구한다. (times)
- !!!, 3번 factorial이 반복되는 경우 factorial_sequence 는 2이므로 1을 더하는 것이다.
- factorial_sequence를 이용해서 toReplace 값과 replacement 값을 구한다.
- 그리고 factorial_sequence는 매번 0으로 초기화해줘야 한다.
- replace를 진행해준다.
08. ANGLE UNIT
- JS의 Math의 삼각함수를 사용할 때는 degree가 아닌 radian이 default이다.
- 즉 Math.cos(매개변수(ex.180) : radian 값이 들어간다.)
- 그리고 inverse 삼각함수는 radian 값을 리턴한다.
- 1deg는 pie rad / 180이다.
- rad, deg 버튼에 따라서 radian 값이 토글된다.
trigo(Math.cos, 180);
function trigo( callback, angle){
if(!RADIAN){ // Degree인 경우 Math.PI/180을 곱해서 degree 값으로 변환
angle = angle * Math.PI/180;
}
return callback(angle);
}
function inv_trigo(callback, value){
let angle = callback(value);
if(!RADIAN){ // Degree인 경우 180/Math.PI를 곱해서 degree 값으로 변환
angle = angle * 180/Math.PI;
}
return angle
}