Stack 과 Queue를 배운뒤 괄호가 이것들로 처리된다는 걸 보고
문득 이제 계산기를 만들 수 있겠다 싶었다.
찾아보니 많은 자료들이 Swing이란 것을 이용한 것 같은데 나는 초보라 알지도 못하고
보니 실무에서 많이 안쓰는지 책에도 내용이 개정되어 빠졌다 ㅠㅠ
그래서 그냥 처음 내가 생각한대로 구현해보자 마음먹었다.
하지만 막상 해보니 1+2 나 숫자 2개를 입력받아 처리는 매우 쉬웠지만 내가 원하는대로 구현은 상당히 어려웠다.
(이상한 집념이 생겨서 이틀이나 날려먹었다....)
게다가 이틀이나 고생했는데 아직도 고쳐야 할 부분이 있다.
(어쩌면 내가 못찾은 문제가 더 있을지도....)
기본적으로 구상한것은 이랫다
1. 괄호의 처리
- Stack을 활용하여 ((((를 push하고 ) 이 처음 pop 되는 순간에 (와 )의 중간 수식을 Middle로 뽑아낸다
- Middle 수식을 사칙연산에 넣어서 처리 후
- 새로운 Middle 값을 (Middle)에 전부 대체해 괄호와 이전 수식 제거
- 또 다시 수행하며 )이 pop 될떄 Middle 다시 처리 반복
2. 뽑아낸 Middle의 처리(이게 난관일줄이야... 갈 길이 멀다)
- 제일 중요한 것이 문자열을 숫자와 부호로 구분하는 것이였다
- 한참 고민하고 구글링을 하며 split + 정규표현식 이라는 것을 사용하면 가능하다고 해서 적용했다
String[] numtmp = Middle.split("(?<=[*/+-])|(?=[*/+-])");//정규연산자
- 위 코드를 적용하면 아마 */+- 인것과 아닌것으로 구분되고 ?는 앞에 전체를 구분해서 잘라내는것 같다
- 처음에는 정규표현식을 \w(알파벳+숫자) \W(그 반대) 로 써서 뽑아냈는데
- 구현하고 보니 치명적인 단점이 나눗셈을 위해 반환을 Double로 해놓아서 100.0 같은 ( . ) 이 구분되어 고통받았다
- 이렇게 구분한 것들을 LinkedList 에 넣고 숫자와 부호로 나누고
- 부호의 갯수를 count 해서 이때 곱하기+나누기의 횟수 를 저장 후 count만큼 곱하기 나누기를 먼저 수행하도록 함
코드를 작성한 후 돌려보니 먼가 흐뭇하게 돌아가서 잠시 기뻣다 ㅎㅎ
하지만 돌리다보니 오류가 나는 점이 발견되고 스스로도 에러라 생각되는 점도 생각하게 됐다.
- '(' 과 ')' 의 갯수가 서로 일치하지 않을때의 Exception 필요
- -5*2 와 같은 음수로 시작하는 Middle
- 2*-5 와 같이 음수로 끝나는 수식이 포함된 Middle (아직 수정못했다... ㅠㅠ)
1 의 에러는 Exception 만 적절히 해주면 해결되서 다행이였지만
2 와 3의 에러는 골치가 아팠다.
기본적으로 음수도 적용되는 계산기라면 저것들은 오류라기보단 뭔가 코드를 수정해야하는 결함에 가까운것 같다.
그래서 고민끝 해결책으로
2의 경우는 0-5*2 와 동일한 수식이라는 점을 해결책으로 찾았다
3의 경우도 뭔가 해결책을 찾아야하는데... 이틀 고민했더니 다른 공부가 너무 밀려버려서
이제 그만하고 생산적인 일을 해야하지 않을까 하는 생각(=변명 ㅎㅎ) 이 들어서
나중에 초보를 벗어나면 다시 더 간결한 코드로 새로짜야겠다 생각했다.
간단한 계산기 구현이였는데도 내가 짠 코드를 내가 헷갈릴 정도로 어지러워졌고 코드가 너무 조잡한거같다.
그래도 덕분에 객체 지향이라는게 얼마나 편리한지
그리고 String 이나 배열 보다 StringBuffuer, LinkedList가 사용면에서 얼마나 더 편리한지 몸소 느끼게 됐다.
아직 초보도 진입을 못했구나 하는 나의 현실을 여실히 느끼며 다시 공부에 전념해야겠다.
<아직 미해결 문제점 : 곱하기와 나누기 뒤에 음수면 에러>
import java.util.EmptyStackException;
import java.util.LinkedList;
import java.util.Stack;
//고쳐야 할 점 : 음수를 뒤에서 곱하거나 나누면 에러!!!!!
public class make_calcul_2st {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyCaluCulLator SWver1 = new MyCaluCulLator();
StringBuffer input = new StringBuffer("(-2*3+2-2*3-3+7-6*7)*4/(3+2)+3/2-5");
try {
System.out.println("노력의 결실이 나옵니다!!!! 답은?"+SWver1.RG(input));
} catch (EmptyStackException | MyCaluCulLator.OverStackException e) {
System.out.println("괄호가 맞지 않습니다");
}
}
}
/*
* 필요사항
* 1. 괄호 계산이 1순위
* 2. *와 / 우선순위
* 3. -A & B 계산시 동시처리
* => 숫자를 쌓기엔 어레이리스트
* => 부호를 찾기엔 스택? 문제는 찾으면 날라감
* =>
*/
class MyCaluCulLator{
String input;
MyCaluCulLator(){}
MyCaluCulLator(String input){
this.input = input ;
}
//-----------------------------------생성자 초기화-----------------------------------
//-------------------------------------괄호 없는 경우 String 수식의 계산------------------------
public String RealCalCul(String Middle) {
System.out.println("-----------------수식"+Middle+"--------------------");
String result="";
String[] numtmp = Middle.split("(?<=[*/+-])|(?=[*/+-])");//정규연산자
boolean nobuho =true;
for (int i = 0; i < numtmp.length; i++) {
if(isbuho(numtmp[i])) {
System.out.println("부호가 있네요");
nobuho = false;
break;
}
}
if(nobuho){ return Middle; }
LinkedList<String> num = new LinkedList<>();
LinkedList<String> buho = new LinkedList<>();
int multidivi_count=0;
int minus = 0;
for(String a: numtmp) {
if(isbuho(a)) {
buho.add(a);
if(a.equals("*")||a.equals("/")) multidivi_count++;
else if(a.equals("-")) minus++;
}
else {
num.add(a);
}
}
int plmicount = buho.size() - multidivi_count - minus;
System.out.println(multidivi_count+"번 곱셈나눗셈 수행할게요");
//부호와 숫자 분리 완료
//곱하기 나누기 횟수 저장 후 양수면 먼저수행
if(multidivi_count>0||plmicount>0) {
while(multidivi_count!=0) {
int index = buho.indexOf("*");
System.out.println(index);
if(index>=0) {
System.out.println("곱하기 수행");
String a = ""+TheCal(num.get(index),"*",num.get(index+1));
num.remove(index);
num.remove(index);
num.add(index, a);
buho.remove(index);
System.out.println(num.get(index));
multidivi_count--;
}
int index2 = buho.indexOf("/");
System.out.println(index2+"=index2값");
if(index2>=0) {
System.out.println(num.get(index2));
System.out.println("나누기 수행");
String ac = ""+TheCal(num.get(index2),"/",num.get(index2+1));
num.remove(index2);
num.remove(index2);
num.add(index2, ac);
buho.remove(index2);
System.out.println(num.get(index2));
multidivi_count--;
}
}//곱하기 while끝
//곱하기 나누기 완료
//더하기 수행
System.out.println("현재값");
for(String a: num) {
System.out.println(a);
}
for(String a: buho) {
System.out.println(a);
}
while(minus!=0) {
int index4 = buho.indexOf("-");
if(index4>=0) {
System.out.println(num.get(index4));
System.out.println("빼기 수행");
String ab = ""+TheCal(num.get(index4),"-",num.get(index4+1));
num.remove(index4);
num.remove(index4);
num.add(index4, ab);
buho.remove(index4);
System.out.println(num.get(index4));
minus--;
}
}
while(plmicount!=0) {
int index3 = buho.indexOf("+");
if(index3>=0) {
System.out.println(num.get(index3));
System.out.println("더하기 수행");
String a = ""+TheCal(num.get(index3),"+",num.get(index3+1));
num.remove(index3);
num.remove(index3);
num.add(index3, a);
buho.remove(index3);
System.out.println(num.get(index3));
plmicount--;
}
}
}
System.out.println("결과는!!!!!!"+num.get(0));
Middle = num.get(0);
return Middle;
}
/*-----------------------------수식에 괄호가 있는 경우 추출 메서드----------------------------*/
public String RG(StringBuffer input2)
throws MyCaluCulLator.OverStackException {
Stack st = new Stack(); //괄호 쌍 검사용 임시 스택
int startindex=0;
int endindex=0;
int count = 0;
int count1 = 0;
for (int i = 0; i < input2.length(); i++){
if((input2.charAt(i)=='(')){
st.push('(');
count1++;
startindex=i;
}//end
else if (input2.charAt(i)==')') {
st.pop();
count1--;
endindex=i;
String middle =input2.substring(startindex+1, endindex);
System.out.println("이번 괄호는 "+middle+"입니다");
char[] c = middle.toCharArray();
for (int j = 0; j < c.length; j++) {
boolean minor = isbuho(c[j]+"");
if(j==0&&minor){
middle = "0"+middle;
System.out.println("음수감지 0추가"+middle);
}
}
middle = RealCalCul(middle);
c = middle.toCharArray();
for (int j = 0; j < c.length; j++) {
boolean minor = isbuho(c[j]+"");
if(j==0&&minor){
middle = "0"+middle;
System.out.println("음수감지 0추가"+middle);
}
}
input2 =input2.replace(startindex, endindex+1,middle);
RG(input2);
break;
}//end
}
String input3 = input2.toString();
if(count1==0) {
char[] c= input3.toCharArray();
for (int j = 0; j < c.length; j++) {
boolean minor = isbuho(c[j]+"");
if(minor){
count++;
}
}
System.out.println("마지막 남은 부호 처리구간, 부호 갯수:"+count);
System.out.println(input3);
if(count!=0) {
input3 = RealCalCul(input3);
}
}
return input3;
}
/*---------------------------String A&B의 계산 메서드---------------------------*/
public double TheCal(String A , String b, String B) {
double a = Double.valueOf(A);
double c = Double.valueOf(B);
double result=0;
switch(b) {
case "+" : result = sum(a,c); break;
case "-" : result =sub(a,c);break;
case "/" : result =divide(a,c);break;
case "*" : result =multi(a,c);break;
}
double per = Double.parseDouble(String.format("%.4f",result));
return per;
}
/*--------------------------연산자 문자인지 확인 BOOLEAN------------------------*/
public boolean isbuho(String d) {
if(d.equals("+")) return true;
else if(d.equals("-")) return true;
else if(d.equals("/")) return true;
else if(d.equals("*")) return true;
else return false;
}
/*-------------------------기본 4칙연산-------------------------------------*/
public double sum(double a, double b) {
return a+b;
}
public double sub(double a, double b) {
return a-b;
}
public double multi(double a, double b) {
return a*b;
}
public double divide(double a, double b) {
return a/b;
}
/*-------------------------괄호 남는 예외-------------------------------------------*/
class OverStackException extends Exception{
OverStackException(String msg){
super(msg);
}
}
}