Coding History

국비 지원 IT(웹앱개발) 취업반 강의 14일차(다항식 계산기 만들기 과정)

BlackBirdIT 2024. 6. 25. 20:34

다항식 게산기 개발 과정을 경험하고 있다.

public class Calc {
    public static int run(String exp) {

        boolean needToMulti = exp.contains("*");
        boolean needToSum = exp.contains("+");
//        boolean needToSub = exp.contains("-");

        int sum = 0;
//        int sub = 0;
        int mult = 1;
        if(needToSum && needToMulti) {
            String[] bits = exp.split(" \\+ ");
            for(int i = 0; i < bits.length; i++) {
                if(needToMulti) {
                    mult *= Integer.parseInt(bits[i]);
                }
                if(needToSum) {
                    sum += Integer.parseInt(bits[i]);
                }
            }
            return sum + mult;
        }
        else if(needToSum) {
            exp = exp.replace("- ", "+ -");

            String[] bits = exp.split(" \\+ ");

            for (String bit : bits) {
                sum += Integer.parseInt(bit);
            }

            return sum; // 리턴값 rs.

        }
//        else if(needToSub) {
//
//            String[] bits = exp.split(" \\- ");
//
//            for (String bit : bits) {
//                sub -= Integer.parseInt(bit);
//            }
//            return sub;
//        }
        else if(needToMulti){
            String[] bits = exp.split(" \\* ");
            for (String bit : bits) {

                mult *= Integer.parseInt(bit);

            }
            return mult;
        }
        return sum + mult;
//        throw new RuntimeException("해석불가 : 올바른 식이 필요해.");
    }
}

대중 이까지 왔는데 여기까지 과정은

순수 더하기 / 빼기 : 처리 가능

순수 곱하기 : 처리가능 -> 근데 빼기가 고장함

복합구조 처리 불가능

이 상태이다.

public class Calc {
    public static int run(String exp) {

        boolean needToMulti = exp.contains("*");
        boolean needToSum = exp.contains("+");
        boolean needToCompound = needToSum && needToMulti; //추가한 코드
//        boolean needToSub = exp.contains("-");

        int sum = 0;
//        int sub = 0;
        int mult = 1;
        if(needToCompound) { //추가한 코드
            String[] bits = exp.split(" \\+ ");

            return Integer.parseInt(bits[0]) + run(bits[1]); 
        }
        else if(needToSum) {
            exp = exp.replace("- ", "+ -");

            String[] bits = exp.split(" \\+ ");

            for (String bit : bits) {
                sum += Integer.parseInt(bit);
            }

            return sum; // 리턴값 rs.

        }
//        else if(needToSub) {
//
//            String[] bits = exp.split(" \\- ");
//
//            for (String bit : bits) {
//                sub -= Integer.parseInt(bit);
//            }
//            return sub;
//        }
        else if(needToMulti){
            String[] bits = exp.split(" \\* ");
            for (String bit : bits) {

                mult *= Integer.parseInt(bit);

            }
            return mult;
        }
        return sum + mult;
//        throw new RuntimeException("해석불가 : 올바른 식이 필요해.");
    }
}

//int mult = Arrays.stream(bits).
//        mapToInt(Integer::parseInt).
//        reduce(1, (a, b) -> a * b);

이렇게 하면 일단 10 + 5 * 2의 테스트 케이스는 통과할 수 있다.
하지만 궁극적으로 복합연산은 되지 않는다.

일단은 추가한 코드가 작동하는 방식은 변수를 다시 run 메소드에 실행시켜서 needToMulti까지 (5 * 2)가 오게 된다. 그리고 그 값을 다시 더한 값을 리턴한다.

일단은 테스트 케이스를 통과하는게 목표라고 하셨는데, 일단 정답을 계속 찾으려고 해서 저렇게 해결을 먼저 한다는 것은 생각하지 못했다.

다음 테스트 케이스는 "20 + 10 + 5 * 2 == 40"을 통과시키는 것이 목표다.

if(needToCompound) {
            String[] bits = exp.split(" \\+ ");

            return Integer.parseInt(bits[0]) + Integer.parseInt(bits[1]) + run(bits[2]);
        }

일단 이렇게 하면 저 테스트 케이스만 통과한다. 하지만 그 전의 테스트 케이스가 통과하지 못한다.
이제는 꼼수가 먹히지 않는 다는 것이니 올바른 구현이 필요하다.

public static int run(String exp) {

        boolean needToMulti = exp.contains("*");
        boolean needToSum = exp.contains("+");
        boolean needToCompound = needToSum && needToMulti;
//        boolean needToSub = exp.contains("-");

        int sum = 0;
//        int sub = 0;
        int mult = 1;
        int rs = 0;

        if(needToCompound) {
            String[] bits = exp.split(" \\+ ");

            char multi = '*'; //곱셈유무 확인을 위해 변수 선언


            for(int i = 0; i < bits.length; i++) { //데이터 순회.
                if(bits[i].indexOf(multi) != -1){ //곱셈 유무 확인.
                    rs = run(bits[i]); //곱셈이 포합된 데이터 값 run으로 돌려서 곱한 값 rs에 저장.
                }
            }
            for(int i = 0; i < bits.length; i++) {
                if(bits[i].indexOf(multi) == -1){ //곱셈 유무 확인
                    System.out.println(bits[i]);
                    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(needToSub) {
//
//            String[] bits = exp.split(" \\- ");
//
//            for (String bit : bits) {
//                sub -= Integer.parseInt(bit);
//            }
//            return sub;
//        }
        else if(needToMulti){
            String[] bits = exp.split(" \\* ");
            for (String bit : bits) {

                mult *= Integer.parseInt(bit);

            }
            return mult;
        }
        return sum + mult;
//        throw new RuntimeException("해석불가 : 올바른 식이 필요해.");
    }
}

이제는 빼기 버그만 수정하면 된다.

저기 위에 흔적이 남아있는데 처음에는 막 뭔가 계속 써보다가 문득 든 생각이 아 이미 더하기에 만들어져 있구나? 라는 생각이 들어서 그냥 -만 있을 때 needToSum 코드가 작동하도록 else if의 조건만 다르게 해서 같은 코드를 넣었더니 모든 테스트 케이스가 통과했다.

public class Calc {
    public static int run(String exp) {

        boolean needToMulti = exp.contains("*");
        boolean needToSum = exp.contains("+");
        boolean needToCompound = needToSum && needToMulti;
        boolean needToSub = exp.contains("-");

        int sum = 0;
        int sub = 0;
        int mult = 1;
        int rs = 0;

        if (needToCompound) {
            String[] bits = exp.split(" \\+ ");

            char multi = '*';


            for (int i = 0; i < bits.length; i++) {
                if (bits[i].indexOf(multi) != -1) {
                    System.out.println(bits[i]);
                    rs = run(bits[i]);
                }
            }
            for (int i = 0; i < bits.length; i++) {
                if (bits[i].indexOf(multi) == -1) {
                    System.out.println(bits[i]);
                    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; // 리턴값 rs.

        } else if (needToSub) {
            exp = exp.replace("- ", "+ -");

            String[] bits = exp.split(" \\+ ");

            for (String bit : bits) {
                sub += Integer.parseInt(bit);
            }
            return sub;
        } else if (needToMulti) {
            String[] bits = exp.split(" \\* ");
            for (String bit : bits) {

                mult *= Integer.parseInt(bit);

            }
            return mult;
        }
        if (mult == 1) {
            mult = 0;
        }
        return sum + mult + sub;
//        throw new RuntimeException("해석불가 : 올바른 식이 필요해.");
    }
}

아래 코드는 강사님의 풀이이다.

//단일항이면 int로 리턴
if (!exp.contains(" "){
    return Interger.parseInt(exp);
}
if (needToCompound) {
    String[] bits = exp.split(" \\+ ");

    String newExp = Arrays.stream(bits)
            .mapToInt(Calc::run) // Calc.run("20"), Calc.run("10") 위로 올라감.
            //Calc.run(5 * 2)-> 곱하기 실행해서 10값받음.
            .mapToObj(e -> e +"")    //정수만 존재했던 것을 String으로 전환.
            .collect(Collectors.Joining(" + "));// String (20) (10) (10)에 " + " 추가해서 
            // --> "20 + 10 + 10"

return run(newExp); //-->"20 + 10 + 10"을 run메소드로 실행해서 결과값 도출
}

10 * 20 + 10 + 5 * 2 == 220 이 테스트 케이스가 통과하지 못하길래 코드를 살펴봤는데 rs 값이 곱셈이 두개 있을 때 후발주자 변수때문에 초기화 되더라. 그래서 rs += 으로 바꿔주었다. 그렇게 테스트 케이스 통과.

if (needToCompound) {
            String[] bits = exp.split(" \\+ ");

            char multi = '*';


            for (int i = 0; i < bits.length; i++) {
                if (bits[i].indexOf(multi) != -1) {
                    System.out.println(bits[i]);
                    rs += run(bits[i]);
                }
            }
            for (int i = 0; i < bits.length; i++) {
                if (bits[i].indexOf(multi) == -1) {
                    System.out.println(bits[i]);
                    sum += Integer.parseInt(bits[i]);
                }
            }
            return sum + rs;
}

다음 테스트 케이스는 (10 + 20) == 30 이였는데 needToSum에 코드 두줄만 넣었더니 해결됐다.

exp = exp.replace("(", "");
exp = exp.replace(")", "");

그냥 괄호만 삭제하면 된다.

public class Calc {
    public static int run(String exp) {

        boolean needToMulti = exp.contains(" * ");
        boolean needToSum = exp.contains(" + ")||exp.contains(" - ");
        boolean needToCompound = needToSum && needToMulti;


        int sum = 0;
        int mult = 1;
        int rs = 0;

        if (needToCompound) {
            String[] bits = exp.split(" \\+ ");

            char multi = '*';


            for (int i = 0; i < bits.length; i++) {
                if (bits[i].indexOf(multi) != -1) {
                    System.out.println(bits[i]);
                    rs += run(bits[i]);
                }
            }
            for (int i = 0; i < bits.length; i++) {
                if (bits[i].indexOf(multi) == -1) {
                    System.out.println(bits[i]);
                    sum += Integer.parseInt(bits[i]);
                }
            }
            return sum + rs;
        } else if (needToSum) {
            exp = exp.replace("(", "");
            exp = exp.replace(")", "");
            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("해석불가 : 올바른 식이 필요해.");
    }
}

강사님이 빼기 버그 해결하시는거 보여주고 바로 고쳤다. boolean에 needToSub를 넣을게 아니고 sum에 한줄 추가하면 되는 문제였다. 생각해보니까 같은 코드 갖다가 쓰면서 왜 저 생각을 못했을까.

아무튼 다음 테스트 케이스는 ((10 + 20)) == 30, (((10 + 20))) == 30 겹괄호다.
예상대로 나는 그냥 바로 통과된다. 코드에서 괄호를 지우도록 시켰으니까.

일단 강사님이 설명하시는걸 듣고 있는데 역시 너무 쉽길래 뭔가 이상하다 했다. 새로운 메서드 까지 등장했다.

exp = stripOuterBrackets(exp);

일단 런 메서드 맨 위에 메서드 실행문을 넣었고 메서드는

private static String stripOuterBrackets(String exp) {
            if (exp.charAt(0) == '(' && exp.charAt(exp.length() - 1) == ')') {
                exp = exp.substring(1, exp.length() - 1);
            }

        return exp;
    }

이런 모양이다. 겹괄호도 해결하기 위해 for문을 추가했다.

private static String stripOuterBrackets(String exp) {
        for (int i = 0; i < exp.length(); i++) {
            if (exp.charAt(0) == '(' && exp.charAt(exp.length() - 1) == ')') {
                exp = exp.substring(1, exp.length() - 1);
            }
        }
        return exp;
    }

이 까지는 했는데,

@Test
    @DisplayName("(20 + 20) + 20 == 60")
    void t18() {
        assertThat(Calc.run("(20 + 20) + 20")).isEqualTo(60);
    }
    @Test
    @DisplayName("(20 + 20) * 2 == 80")
    void t19() {
        assertThat(Calc.run("(20 + 20) * 2")).isEqualTo(80);
    }

이 테스트 케이스가 문제다. 이 괄호를 어떻게 지워야될지 모르겠다. 아니, 괄호 까지는 지울 수 있는데 공백이 남아서 공백 처리를 하고 싶은데 그게 안된다.

private static String stripOuterBrackets(String exp) {
        char s1 = '(';
        char s2 = ')';
        String rs = "";

        for (int i = 0; i < exp.length(); i++) {
            if (exp.indexOf(s1) != -1) {
                exp = exp.replace(s1, ' ');
            }
        }

        for (int i = 0; i < exp.length(); i++) {
            if (exp.indexOf(s2) != -1) {
                exp = exp.replace(s2, ' ');
            }
        }


        return exp;
    }
}

이렇게 하면 char 값을 무조건 넣어야되서 띄어쓰기를 해야되고 그럼 괄호가 없어진 자리에 공백이 남는다.

그래서 공백을 없애려고

for(int i = 0; i < exp.length(); i++) {

            if(exp.charAt(i) != ' ')
                //공백이 제거된 문자열을 result변수에 저장
                rs += exp.charAt(i);
        }
        exp = rs;

이 코드를 추가 했는데 이렇게 하니까 수식 전부에 공백을 없애버려서 앞에 작성한 코드들이 죄다 먹통이 된다.
내가 원하는 곳의 특정 공백만 없애는 방법이 필요한데 그걸 찾지 못하고 있다.

겹괄호 없애는 강사님 코드는

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

나랑 쓰는 방법이 달라서 그렇지 궁극적으로는 비슷하긴 한데 괄호가 없을 경우에는 값을 그냥 받는 코드도 있다.

아무튼 오늘의 수업은 여기까지였고 저걸 해결하는 방법을 다방면으로 생각해 가면 좋을 것 같다.