From 26b1bc82fc75d18ffd798d190b96276090f009bb Mon Sep 17 00:00:00 2001 From: haebun Date: Tue, 1 Apr 2025 14:12:01 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=82=AC=EC=B9=99=EC=97=B0=EC=82=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EB=82=98=EB=88=88=20=EB=82=98=EB=A8=B8=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기능 5개를 구현하고자 하여 사칙연산과 나눈 나머지 값을 구하는 기능을 구현 - 연산자 우선 순위를 적용하여 연산에 대한 조사를 실시하여 적용 - 테스트 코드에 나눈 나머지, 음수 연산(첫 숫자) 추가 --- README.md | 19 +- src/main/java/calculator/Application.java | 14 +- src/main/java/calculator/Calculator.java | 221 +++++++++++++++++- src/test/java/calculator/ApplicationTest.java | 13 +- 4 files changed, 255 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 22621e3..6ab3a95 100644 --- a/README.md +++ b/README.md @@ -1 +1,18 @@ -# Calculator +# 계산기 +사칙연산 및 나머지 연산을 문자열로 입력받아 수행하는 계산기입니다. + +## 목차 +- [기능](#기능) +- [사용법](#사용법) + +## 기능 +- **덧셈 (+)**: 두 숫자를 더합니다. +- **뺄셈 (-)**: 두 숫자를 뺍니다. +- **곱셈 (\*)**: 두 숫자를 곱합니다. +- **나눗셈 (/)**: 두 숫자를 나눕니다. (0으로 나누기 예외 처리 포함) +- **나눈 나머지 (%)**: 두 숫자를 나눈 나머지를 구합니다. +- **종료(e)** + +## 사용법 +- Application을 실행하여 키보드 입력을 통해 사용 +- Test 코드를 수행하여 수행 결과를 확인 \ No newline at end of file diff --git a/src/main/java/calculator/Application.java b/src/main/java/calculator/Application.java index d909a50..d4ebc1c 100644 --- a/src/main/java/calculator/Application.java +++ b/src/main/java/calculator/Application.java @@ -1,11 +1,19 @@ package calculator; +import java.util.Scanner; + public class Application { public static void main(String[] args) { - // TODO: 코드 작성 + Scanner scanner = new Scanner(System.in); - // 예시 코드 Calculator calculator = new Calculator(); - calculator.calculate(""); + + while (true) { + String string = scanner.nextLine(); + System.out.println("수식을 입력해 주세요"); + + if(string.contains("e")) break; + System.out.println(calculator.calculate(string)); + } } } diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index 88e8552..dab1d47 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -1,13 +1,220 @@ package calculator; -/* - 1주차에만 제공되는 예시 코드입니다. - 코드는 그대로 사용해도 되고 수정해도 됩니다. - */ +import java.util.ArrayList; +import java.util.List; public class Calculator { + List values; + List operator; + + public Calculator() { + values = new ArrayList<>(); + operator = new ArrayList<>(); + } + + /** + * 1. 숫자와 연산자를 분리합니다. + * 2. 계산할 수 있는 수식인지 검사합니다. + * (숫자 개수 = n, 연산자 계수 = n-1) + * 3. 우선순위에 맞게 계산을 수행합니다. + * 4. 출력합니다. + */ public int calculate(String input) { - // TODO: 코드 구현 - throw new IllegalArgumentException("아직 구현되지 않았습니다."); + // 0. 이전 계산 상태 초기화 + values.clear(); + operator.clear(); + + // 1. 숫자와 연산자 분리 + stringParser(input); + + // 2. 유효성 검사 + if (values.isEmpty()) { + if (operator.isEmpty()) return 0; + else throw new IllegalArgumentException("수식에 숫자가 없습니다."); + } + + if (!operator.isEmpty() && values.size() != operator.size() + 1) { + throw new IllegalArgumentException("잘못된 수식 형태입니다. 숫자와 연산자의 개수가 맞지 않습니다."); + } + + if (operator.isEmpty()) { + return values.get(0); + } + + // 3. 계산 실행 (연산자 우선순위대로 적용) + for (int i = 1; i <= 15; i++) { + calculatePass(i); + } + + // 4. 최종 결과 반환 + if (values.size() == 1 && operator.isEmpty()) { + return values.get(0); + } else { + // 계산 로직 오류 또는 잘못된 수식 처리 실패 시 + System.err.println("계산 후 남은 값: " + values); + System.err.println("계산 후 남은 연산자: " + operator); + throw new IllegalArgumentException("계산 로직 오류: 최종 결과 도출에 실패했습니다."); + } + } + + /** + * 문자열을 숫자와 연산자를 분리하고 리스트에 저장합니다. + *

+ * 1.문자가 숫자일 때, 버퍼에 저장합니다. + * 2.숫자도, 연산자도 아닐 때 예외 처리합니다. + * 3.숫자가 나오기 전에 먼저 연산자가 나온 경우 + * 3. 버퍼를 초기화하고 저장된 숫자를 리스트에 추가합니다. + */ + private void stringParser(String input) { + StringBuilder currentNumber = new StringBuilder(); + + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + + if (Character.isDigit(c)) { + currentNumber.append(c); + continue; + } + + if (!isOperator(c)) + throw new IllegalArgumentException("지원하지 않는 문자입니다: " + c); + + if (c == '-' && (values.isEmpty() || (i > 0 && isOperator(input.charAt(i - 1))))) { + currentNumber.append(c); + continue; + } else if (values.isEmpty() && currentNumber.length() == 0) { + throw new IllegalArgumentException("수식이 숫자로 시작해야 합니다 (단, 음수 부호 제외): " + input); + } + + if (currentNumber.length() > 0) { + values.add(Integer.parseInt(currentNumber.toString())); + currentNumber.setLength(0); + } + + operator.add(c); + } + + if (currentNumber.length() > 0) { + values.add(Integer.parseInt(currentNumber.toString())); + } + } + + /** + * 지정된 우선순위에 해당하는 연산을 수행합니다. + * 리스트를 뒤에서부터 순회하여 인덱스 문제를 방지합니다. + */ + private void calculatePass(int priority) { + for (int i = operator.size() - 1; i >= 0; i--) { + char op = operator.get(i); + if (getPriority(op) == priority) { + // 해당 우선순위의 연산자를 찾으면 + int left = values.get(i); + int right = values.get(i + 1); + int result = operation(left, right, op); + + values.set(i, result); + values.remove(i + 1); + operator.remove(i); + } + } + } + + /** + * 두 숫자와 연산자 하나를 받아 실제 계산을 수행 + */ + private int operation(int a, int b, char op) { + switch (op) { + case '+': + return add(a, b); + case '-': + return subtract(a, b); + case '*': + return multiply(a, b); + case '/': + return divide(a, b); + case '%': + return modulo(a, b); + default: + throw new IllegalArgumentException("알 수 없는 연산자: " + op); + } + } + + /** + * 해당 문자가 기능에 포함된 연산자인지 확인하는 헬퍼 메소드 + */ + private boolean isOperator(char c) { + switch (c) { + case '+': + case '-': + case '*': + case '/': + case '%': + return true; + default: + return false; + } + } + + /** + * 연산자 우선 순위를 반환합니다. (값이 작을수록 우선순위가 높도록 재정의) + * https://en.wikipedia.org/wiki/Order_of_operations#Programming_languages + * 3: *, /, % + * 4: +, - + */ + public int getPriority(char c) { + switch (c) { + case '*': + case '/': + case '%': + return 3; + case '+': + case '-': + return 4; + default: + return 15; + } + } + + /** + * 덧셈 계산 + */ + public int add(int a, int b) { + return a + b; + } + + /** + * 뺄셈 계산 + */ + public int subtract(int a, int b) { + return a - b; + } + + /** + * 곱셈 계산 + */ + public int multiply(int a, int b) { + return a * b; + } + + /** + * 나눗셈 계산 + */ + public int divide(int a, int b) { + if (b == 0) { + System.out.println("0으로 나눌 수 없습니다."); + throw new IllegalArgumentException("0으로 나눌 수 없습니다."); // 예외 메시지 일관성 + } + return a / b; + } + + /** + * 나눈 나머지 계산 + */ + public int modulo(int a, int b) { + if (b == 0) { + System.out.println("0으로 나눌 수 없습니다."); + throw new IllegalArgumentException("0으로 나눌 수 없습니다."); // 예외 메시지 일관성 + } + return a % b; } -} +} \ No newline at end of file diff --git a/src/test/java/calculator/ApplicationTest.java b/src/test/java/calculator/ApplicationTest.java index f3e71c7..7d8ca89 100644 --- a/src/test/java/calculator/ApplicationTest.java +++ b/src/test/java/calculator/ApplicationTest.java @@ -33,5 +33,16 @@ public class ApplicationTest { }); } - // TODO: 테스트 코드 추가 가능합니다. + @Test + public void 나눈_나머지를_포함한_수식_계산() { + int result = calculator.calculate("5%2+10"); + assertThat(result).isEqualTo(11); + } + + @Test + public void 음수로_시작하는_수식_계산() { + int result = calculator.calculate("-10+50*5"); + assertThat(result).isEqualTo(240); + } + }