-(8 + 2) * -(7 + 3) + 5 == 105 테스트케이스 통과 (강사님 코드)
package org.koreait;
import java.util.Arrays;
import java.util.stream.Collectors;
public class Calc {
public static boolean debug = true;
public static int runCallCount = 0;
public static int run(String exp) {
runCallCount++;
exp = exp.trim(); // 양 옆의 쓸데없는 공백 제거
// 괄호 제거
exp = stripOuterBrackets(exp);
// 만약에 -( 패턴이라면, 내가 갖고있는 코드는 해석할 수 없으므로 해석할 수 있는 형태로 수정
int[] pos = null;
while ((pos = findCaseMinusBracket(exp)) != null) {
exp = changeMinusBracket(exp, pos[0], pos[1]);
}
exp = stripOuterBrackets(exp);
if (debug) {
System.out.printf("exp(%d) : %s\n", runCallCount, exp);
}
// 단일항이 들어오면 바로 리턴
if (!exp.contains(" ")) {
return Integer.parseInt(exp);
}
boolean needToMulti = exp.contains(" * ");
boolean needToPlus = exp.contains(" + ") || exp.contains(" - ");
boolean needToSplit = exp.contains("(") || exp.contains(")");
boolean needToCompound = needToMulti && needToPlus;
if (needToSplit) {
int splitPointIndex = findSplitPointIndex(exp);
String firstExp = exp.substring(0, splitPointIndex);
String secondExp = exp.substring(splitPointIndex + 1);
char operator = exp.charAt(splitPointIndex);
exp = Calc.run(firstExp) + " " + operator + " " + Calc.run(secondExp);
return Calc.run(exp);
} else if (needToCompound) {
String[] bits = exp.split(" \\+ ");
String newExp = Arrays.stream(bits).mapToInt(Calc::run).mapToObj(e -> e + "").collect(Collectors.joining(" + "));
return run(newExp);
}
if (needToPlus) {
exp = exp.replaceAll("- ", "+ -");
String[] bits = exp.split(" \\+ ");
int sum = 0;
for (int i = 0; i < bits.length; i++) {
sum += Integer.parseInt(bits[i]);
}
return sum;
} else if (needToMulti) {
String[] bits = exp.split(" \\* ");
int sum = 1;
for (int i = 0; i < bits.length; i++) {
sum *= Integer.parseInt(bits[i]);
}
return sum;
}
throw new RuntimeException("해석 불가 : 올바른 계산식이 아니야");
}
private static String changeMinusBracket(String exp, int startPos, int endPos) {
String head = exp.substring(0, startPos);
String body = "(" + exp.substring(startPos + 1, endPos + 1) + " * -1)";
String tail = exp.substring(endPos + 1);
exp = head + body + tail;
return exp;
}
private static int[] findCaseMinusBracket(String exp) {
for (int i = 0; i < exp.length() - 1; i++) {
if (exp.charAt(i) == '-' && exp.charAt(i + 1) == '(') {
// 발견
int bracketsCount = 1;
for (int j = i + 2; j < exp.length(); j++) {
char c = exp.charAt(j);
if (c == '(') {
bracketsCount++;
} else if (c == ')') {
bracketsCount--;
}
if (bracketsCount == 0) {
return new int[]{i, j};
}
}
}
}
return null;
}
private static int findSplitPointIndex(String exp) {
int index = findSplitPointIndexBy(exp, '+');
if (index >= 0) return index;
return findSplitPointIndexBy(exp, '*');
}
private static int findSplitPointIndexBy(String exp, char findChar) {
int bracketsCount = 0;
for (int i = 0; i < exp.length(); i++) {
char c = exp.charAt(i);
if (c == '(') {
bracketsCount++;
} else if (c == ')') {
bracketsCount--;
} else if (c == findChar) {
if (bracketsCount == 0) return i;
}
}
return -1;
}
private static String stripOuterBrackets(String exp) {
if (exp.charAt(0) == '(' && exp.charAt(exp.length() - 1) == ')') {
int bracketsCount = 0;
for (int i = 0; i < exp.length(); i++) {
if (exp.charAt(i) == '(') {
bracketsCount++;
} else if (exp.charAt(i) == ')') {
bracketsCount--;
}
if (bracketsCount == 0) {
if (exp.length() == i + 1) {
return stripOuterBrackets(exp.substring(1, exp.length() - 1));
}
return exp;
}
}
}
return exp;
}
}
강사님 코드를 보면서 확실히 좀 느낀게 자신이 구현해 둔 것에서 최대한 활용해야한다는 것? 지금은 계산기를 만들고 있으니까 내가 연산이 가능한 것들을 정확히 파악하고 수식이 망가지지 않는 선에서 문자열에 변형을 조금씩 준다는게 핵심인 것 같다. 디버그를 통해 정확히 어디가 문제인지 파악하는 것도 당연한 일이다.
3 x 1 + (1 - (4 * 1 - (1 - 1))) == 0 다음이 최종인데, 이거 보다 더 쉬운 5 - (1 + 5) == -1 이것부터 해결해보자.
if (needToSplit) {
int splitPointIndex = findSplitPointIndex(exp);
String firstExp = exp.substring(0, splitPointIndex);
String secondExp = exp.substring(splitPointIndex + 1);
char operator = exp.charAt(splitPointIndex);
exp = Calc.run(firstExp) + " " + operator + " " + Calc.run(secondExp);
return Calc.run(exp);
}
여기에 우리가 이미 만든 거 한줄만 추가 하면 된다.
if (needToSplit) {
exp = exp.replaceAll("- ", "+ -");
int splitPointIndex = findSplitPointIndex(exp);
String firstExp = exp.substring(0, splitPointIndex);
String secondExp = exp.substring(splitPointIndex + 1);
char operator = exp.charAt(splitPointIndex);
exp = Calc.run(firstExp) + " " + operator + " " + Calc.run(secondExp);
return Calc.run(exp);
}
그럼 최종 테스트 케이스도 풀린다.
그래서 강사님의 최종 코드는
import java.util.Arrays;
import java.util.stream.Collectors;
public class Calc {
public static boolean debug = true;
public static int runCallCount = 0;
public static int run(String exp) {
return _run(exp, 0);
}
public static int _run(String exp, int depth) {
runCallCount++;
exp = exp.trim(); // 양 옆의 쓸데없는 공백 제거
// 괄호 제거
exp = stripOuterBrackets(exp);
// 만약에 -( 패턴이라면, 내가 갖고있는 코드는 해석할 수 없으므로 해석할 수 있는 형태로 수정
int[] pos = null;
while ((pos = findCaseMinusBracket(exp)) != null) {
exp = changeMinusBracket(exp, pos[0], pos[1]);
}
exp = stripOuterBrackets(exp);
if (debug) {
System.out.print(" ".repeat(depth * 4));
System.out.printf("exp(%d) : %s\n", runCallCount, exp);
}
// 단일항이 들어오면 바로 리턴
if (!exp.contains(" ")) {
return Integer.parseInt(exp);
}
boolean needToMulti = exp.contains(" * ");
boolean needToPlus = exp.contains(" + ") || exp.contains(" - ");
boolean needToSplit = exp.contains("(") || exp.contains(")");
boolean needToCompound = needToMulti && needToPlus;
if (needToSplit) {
exp = exp.replaceAll("- ", "+ -");
int splitPointIndex = findSplitPointIndex(exp);
String firstExp = exp.substring(0, splitPointIndex);
String secondExp = exp.substring(splitPointIndex + 1);
char operator = exp.charAt(splitPointIndex);
exp = Calc._run(firstExp, depth + 1) + " " + operator + " " + Calc._run(secondExp, depth + 1);
return Calc._run(exp, depth + 1);
} else if (needToCompound) {
String[] bits = exp.split(" \\+ ");
String newExp = Arrays.stream(bits)
.mapToInt(e -> Calc._run(e, depth + 1))
.mapToObj(e -> e + "")
.collect(Collectors.joining(" + "));
return _run(newExp, depth + 1);
}
if (needToPlus) {
exp = exp.replaceAll("- ", "+ -");
String[] bits = exp.split(" \\+ ");
int sum = 0;
for (int i = 0; i < bits.length; i++) {
sum += Integer.parseInt(bits[i]);
}
return sum;
} else if (needToMulti) {
String[] bits = exp.split(" \\* ");
int sum = 1;
for (int i = 0; i < bits.length; i++) {
sum *= Integer.parseInt(bits[i]);
}
return sum;
}
throw new RuntimeException("해석 불가 : 올바른 계산식이 아니야");
}
private static String changeMinusBracket(String exp, int startPos, int endPos) {
String head = exp.substring(0, startPos);
String body = "(" + exp.substring(startPos + 1, endPos + 1) + " * -1)";
String tail = exp.substring(endPos + 1);
exp = head + body + tail;
return exp;
}
private static int[] findCaseMinusBracket(String exp) {
for (int i = 0; i < exp.length() - 1; i++) {
if (exp.charAt(i) == '-' && exp.charAt(i + 1) == '(') {
// 발견
int bracketsCount = 1;
for (int j = i + 2; j < exp.length(); j++) {
char c = exp.charAt(j);
if (c == '(') {
bracketsCount++;
} else if (c == ')') {
bracketsCount--;
}
if (bracketsCount == 0) {
return new int[]{i, j};
}
}
}
}
return null;
}
private static int findSplitPointIndex(String exp) {
int index = findSplitPointIndexBy(exp, '+');
if (index >= 0) return index;
return findSplitPointIndexBy(exp, '*');
}
private static int findSplitPointIndexBy(String exp, char findChar) {
int bracketsCount = 0;
for (int i = 0; i < exp.length(); i++) {
char c = exp.charAt(i);
if (c == '(') {
bracketsCount++;
} else if (c == ')') {
bracketsCount--;
} else if (c == findChar) {
if (bracketsCount == 0) return i;
}
}
return -1;
}
private static String stripOuterBrackets(String exp) {
if (exp.charAt(0) == '(' && exp.charAt(exp.length() - 1) == ')') {
int bracketsCount = 0;
for (int i = 0; i < exp.length(); i++) {
if (exp.charAt(i) == '(') {
bracketsCount++;
} else if (exp.charAt(i) == ')') {
bracketsCount--;
}
if (bracketsCount == 0) {
if (exp.length() == i + 1) {
return stripOuterBrackets(exp.substring(1, exp.length() - 1));
}
return exp;
}
}
}
return exp;
}
}
이렇고 내 코드 최종은
import java.util.Arrays;
import java.util.List;
public class Calc {
public static boolean debug = false;
public static int runCallCount = 0;
public static int run(String exp) {
runCallCount++;
exp = exp.trim();
exp = stripOuterBrackets(exp);
if (debug) {
System.out.printf("exp(%d) : %s\n", runCallCount, exp);
}
// 단일항이 들어오면 바로 리턴
if (!exp.contains(" ")) {
return Integer.parseInt(exp);
}
boolean needToMulti = exp.contains(" * ");
boolean needToSum = exp.contains(" + ") || exp.contains(" - ");
boolean needToCompound = needToSum && needToMulti;
if (exp.contains("(")) { //괄호가 있다면 실행.
int openBracket = exp.indexOf("("); // 괄호 위치 인덱스 번호로 찾기.
int closeBracket = exp.indexOf(")");
int count = 0; //괄호 개수 세기
for (int i = openBracket; i < exp.length(); i++) { //인덱스 길이만큼 반복하면서 괄호 찾기
if (exp.charAt(i) == '(') count++; //괄호 '(' 추적후 세기.
else if (exp.charAt(i) == ')') count--; // ')'가 시작되면 카운트 감소
if (count == 0) { // '(' 와 ')'의 개수가 같아야 식이 성립하기 때문에 0이될 때를 조건으로 삼음.
closeBracket = i; // 반복문 반복 횟수가 괄호 마지막 인덱스이기 때문에 그 값을 closeBraket에 넣어줌.
break; //브레이크
}
}
String expression = exp.substring(openBracket + 1, closeBracket); //첫괄호와 끝괄호 위치를 걷어냄.
int expResult = run(expression); // run메서드 전달 실행.
exp = exp.substring(0, openBracket) + expResult + exp.substring(closeBracket + 1);
//여는 괄호 이전의 수식 + 괄호 수식 결과값 + 닫는 괄호 이후의 수식
return run(exp); // run메서드 전달 실행.
}
int sum = 0;
int mult = 1;
int rs = 0;
if (needToCompound) {
exp = exp.replace("- ", "+ -");
String[] bits = exp.split(" \\+ ");
for (int i = 0; i < bits.length; i++) {
}
char multi = '*';
for (int i = 0; i < bits.length; i++) {
if (bits[i].indexOf(multi) != -1) {
rs += run(bits[i]);
}
}
for (int i = 0; i < bits.length; i++) {
if (bits[i].indexOf(multi) == -1) {
sum += Integer.parseInt(bits[i]);
}
}
return sum + rs;
} else if (needToSum) {
exp = exp.replace("- ", "+ -");
String[] bits = exp.split(" \\+ ");
for (String bit : bits) {
sum += Integer.parseInt(bit);
}
return sum;
} else if (needToMulti) {
String[] bits = exp.split(" \\* ");
for (String bit : bits) {
mult *= Integer.parseInt(bit);
}
return mult;
}
return sum + mult;
// throw new RuntimeException("해석불가 : 올바른 식이 필요해.");
}
private static int findSplitPointIndex(String exp) {
int index = findSplitPointIndexBy(exp, '+');
if (index >= 0) return index;
return findSplitPointIndexBy(exp, '*');
}
private static int findSplitPointIndexBy(String exp, char findChar) {
int brackesCount = 0;
for (int i = 0; i < exp.length(); i++) {
char c = exp.charAt(i);
if (c == '(') {
brackesCount++;
} else if (c == ')') {
brackesCount--;
} else if (c == findChar) {
if (brackesCount == 0) return i;
}
}
return -1;
}
private static String stripOuterBrackets(String exp) {
int outerBracketsCount = 0;
while (exp.charAt(outerBracketsCount) == '(' && exp.charAt(exp.length() - 1 - outerBracketsCount) == ')') {
outerBracketsCount++;
}
if (outerBracketsCount == 0) return exp;
return exp.substring(outerBracketsCount, exp.length() - outerBracketsCount);
}
}
이게 최종이였다.
강사님은 메서드를 많이 쓰셨고 재귀함수를 많이 구현하셨다.
그리고 깜짝 시험을 봤는데 준비를 하나도 안했었다. 나는 내일 볼 줄 알고 오늘 공부하려고 했었는데 역시 미리 준비해야되는가보다.. 아무튼 시험은 망쳤고 아직 내가 많이 모자르다는 것을 체감했다. 세번째 문제는 풀 줄 아는 문제였는데 두번째 문제에서 막힌거 계속 보다가 시간가는 줄 모르고 세번째 문제 내지도 못하고 끝나버렸다. 아마 나는 재시험을 볼 것 같은데 재시험은 꼭 만점 받아야지!!
암튼 그러고 이제 앱 구현하기에 들어갔다. 기본적인 CRUD를 만들어볼 수 있는 기회다.
motivation으로 가닥이 잡혔고 간단한 게시판 만들기와 같을 것이다. 강사님과 함께 하는 프로젝트와 깃은 만들었고 나는 내것도 따로 만들어서 해볼 생각이다.
일단 오늘 시험문제부터 다시 풀어야겠다.
'Coding History' 카테고리의 다른 글
국비 지원 IT(웹앱개발) 취업반 강의 17일차 (motivation 앱 만들기) (1) | 2024.06.30 |
---|---|
2024. 06. 27. 시험 문제 풀이 (0) | 2024.06.27 |
레드벨벳 dumb dumb, 덤을 몇번 불렀을까? (코딩 심심풀이) (0) | 2024.06.27 |
2024. 06. 26 문제풀이 (0) | 2024.06.26 |
국비 지원 IT(웹앱개발) 취업반 강의 15일차 (다항식 계산기 만들기 과정) (0) | 2024.06.26 |