2014년 3월 18일 화요일

앞에서 만든 스택을 이용하여 계산기 프로그램을 만들어 보자.(Java Stack Calc), 구로디지털단지 자바계산기교육,자바스택교육,JAVa스택교육,스택계산기

앞에서 만든 스택을 이용하여 계산기 프로그램을 만들어 보자.(Java Stack Calc), 구로디지털단지 자바계산기교육,자바스택교육,JAVa스택교육,스택계산기

계산기 프로그램을 만든다고 생각해 보자. 물론 아주 쉽게 사칙연산 계산기를 떠올릴 것이다. 
그러나 사용자의 입력을 스트링 타입으로 받고 그 결과를 계산해서 리턴해 주는 프로그램을 생각해 보자. 
예를들어 ((2+3)*4/2 )+(13/2*6-5(56+3)) 를 입력 받았다. 과연 어떻게 해서 프로그램상에서 계산해 낼 것인가? 

위와 같은 연산식을 Infix notation이라고 한다. 왜냐하면 연산자가 숫자의 사이에 위치 하고 있기 때문이다. 
그러나 이러한 식을 바로 계산하기에는 상당한 어려움이 따른다. 

따라서 우리는 이것을 Postfix notation으로 바꾸고 연산하는 방식을 택하기로 한다.


[중위표기]

INFIX       POSTFIX
----------------------------------------
a - b ab-
a + b / c abc/+
a * b + c ab*c+
(a + b) - c ab+c-
a + c - h/b*r                   ac+hb/r*-




-. 중위표기를 후위표기로 바꾸기

어떻게 Postfix notation으로 바꾸는지 생각해 봐야 한다, 물론 Postfix notation으로 바꾸고 난 후에는 어떻게 계산이 되는지도 이해를 해야만 
한다.


1. ‘(‘를 만나면 스택에 푸시한다.
2. ‘)’를 만나면 스택에서 ‘(‘가 나올 때까지 팝하여 출력하고 ‘(‘는 팝하여 버린다.
3. 연산자를 만나면 스택에서 그 연산자보다 낮은 우선순위의 연산자를 만날 때까지 
    팝하여 출력한 뒤에 자신을 푸시한다.(우선순위가 같거나 높은것은 팝한다.)
4. 피연산자는 그냥 출력한다.
5. 모든 입력이 끝나면 스택에 있는 연산자들을 모두 팝하여 출력한다.


-. 후위수식을 스택을 이용하여 계산하기

    Postfix로 표시된 수식을 스택을 이용하여 계산하는 과정

    하나 읽어 피연산자면 스택에 넣는다.
    연산자이면 필요한 수 만큼 피연산자를 스택에서 커낸 후 연산을 수행하고 다시 스택에 넣는다.


[예제]

0. Stack.java

interface Stack {   
           public boolean isEmpty();
           public Object peek();
           public void push(Object theObject);
           public Object pop();
}


1. ArrayStack.java


public class ArrayStack implements Stack   {            
             int top;         // current top of stack
             Object [] stack;   // element array

               
             public ArrayStack(int initialCapacity) {
                  if (initialCapacity < 1)
                  throw new IllegalArgumentException
                                        ("initialCapacity must be >= 1");
                  stack = new Object [initialCapacity] ; 
                  top = -1; 
            }

   
       public ArrayStack() { 
    this(10); 
       }
        
       public boolean isEmpty( ) { 
          return top == -1; 
       }       
       
        public Object peek() {
               if (isEmpty() ) 
                     throw new EmptyStackException();
               return stack[top];
        }     


      
  public void push(Object theElement) {
  // increase array size if necessary    
  if (top == stack.length - 1) ensureCapacity();
         
            // put theElement at the top of the stack  
            stack[++top] = theElement; 
      }


 
     public Object pop() {
            if  (isEmpty())
                  throw new EmptyStackException();
            Object topElement = stack[top];
             stack[top--] = null;   // enable garbage collection
             return topElement;
      }    

 private void ensureCapacity()  {
      Object[] larger = new Object[stack.length*2];

      for (int index=0; index < stack.length; index++)
         larger[index] = stack[index];

      stack = larger;
   }

   public String toString() {
    if (isEmpty())
      return "<empty stack>";
    String result = "<stack :";
    for (int i = top; i >= 0; i--)
      result += stack[i] + " ";
    return result + ">";
  } // end toString
}






2. Calc.java


class Calc {  
   //-------------------------------------------------------------------
   //스택을 이용하여 중위표현을 후위표현으로 바꾸는 메소드
   //-------------------------------------------------------------------
  String  postfix(String infixExp) {
 Double value;
 //숫자의 끝임을 알려주는 flag
 //소수점 수식도 처리하기 위해서...
      boolean endOfNumber = false;   
   String postfixExp = new String();
 ArrayStack stk = new ArrayStack();

for(int i = 0; i < infixExp.length(); i++)
{
 switch(infixExp.charAt(i))
 {
//피연산자는 그대로 출력한다.
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
 postfixExp = postfixExp.concat(infixExp.charAt(i)+"");
 endOfNumber = true;
 break;
//왼쪽괄호인 경우에는 스택에 push 한다.
case '(':
 if(endOfNumber == true)  {
postfixExp = postfixExp.concat(" ");
endOfNumber = false;
 }

 stk.push(new Character('('));
 break;
//우측괄호인 경우 좌괄호가 나올때까지 pop하여 출력하고
   //좌괄호는 pop하여 버린다.
case ')':
 if(endOfNumber == true)  {
postfixExp = postfixExp.concat(" ");
endOfNumber = false;
 }  
 while(((Character)stk.peek()).charValue() != '(' )
postfixExp = postfixExp.concat(((Character)stk.pop()).toString());
 Object openParen = stk.pop();  
 break;
case '+':
case '-':
case '*':
case '/':
 if(endOfNumber == true)  {
postfixExp = postfixExp.concat(" ");
endOfNumber = false;
 }
 //연산자를 만나면 스택에서 그 연산자보다 낮은 우선순위의 연산자를 만날 때까지 
              //팝하여 출력한 뒤에 자신을 푸시한다.(우선순위가 같거나 높은것은 팝한다.)
 while ( !stk.isEmpty() && ((Character)stk.peek()).charValue() != '(' 
           && getPrec(infixExp.charAt(i)) <= getPrec(((Character)stk.peek()).charValue()) )  {  
postfixExp = postfixExp.concat(((Character)stk.pop()).toString());
 }
 stk.push(new Character(infixExp.charAt(i)));
 break;
 }
}

if(endOfNumber == true) {
 postfixExp = postfixExp.concat(" ");
 endOfNumber = false;
}

//모든 작업이 끝나면 스택에 있는 연산자들을 모두 팝하여 출력한다.
while( !stk.isEmpty()) {
 postfixExp = postfixExp.concat(((Character)stk.pop()).toString());
}

System.out.println(postfixExp);

return postfixExp;
  }

  //----------------------------------------------------------------------
  //후위표기 수식을 스택을 이용한 연산을 수행하는 메소드
  //----------------------------------------------------------------------
  Double result(String postfixExp) {
System.out.println(postfixExp);
    Double value, buffer;
String temp = new String();
ArrayStack stk = new ArrayStack();

    for(int i=0; i<postfixExp.length(); i++)    {
        switch(postfixExp.charAt(i))    {

            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '.':
//여기까지는 아직 공백을 만나지 않았으므로 수식의 끝이 아니다.
                temp = temp.concat(postfixExp.charAt(i)+"");
                break;
            case ' ':
//공백을 만나서야 비로서 수식을 스택에 넣는다.
             //공백을 만나기전에 수식이 여러개 있었다면 temp에 붙어서 저장되어 있다.
                stk.push(new Double(temp));
                temp = new String();
                break;
            case '+':
                value = new Double(((Double)stk.pop()).doubleValue() + ((Double)stk.pop()).doubleValue());
                stk.push(value);
                break;
            case '-':
                buffer = new Double(((Double)stk.pop()).doubleValue());
                value = new Double(((Double)stk.pop()).doubleValue() - buffer.doubleValue());
                stk.push(value);
                break;
            case '*':
                value = new Double(((Double)stk.pop()).doubleValue() * ((Double)stk.pop()).doubleValue());
                stk.push(value);
                break;
            case '/':
                buffer = new Double(((Double)stk.pop()).doubleValue());
                value = new Double(((Double)stk.pop()).doubleValue() / buffer.doubleValue());
                stk.push(value);
                break;
        }
    }
return (Double)stk.peek();
  }

  //------------------------------------------
  //연산자의 우선순위를 Return
  //------------------------------------------
  int getPrec(char op) {
    int prec = 0;
    switch (op)   {
      case '+':
      case '-':
        prec = 1;
        break;
      case '*':
      case '/':
        prec = 2;
        break;
    }
    return prec;
  }

    //-----------------------------------------
    //괄호의 정확성 검사
//-----------------------------------------
    static boolean bracketsBalance (String exp) {
ArrayStack stk = new ArrayStack(exp.length() +1); 
for (int i = 0; i < exp.length(); i++) {
//Scan across the __EXPRESSION__
char ch = exp.charAt(i);
if (  ch== '[' || ch == '('  )  {
stk.push( new Character(ch));
}         
else if(ch == ']' || ch == ')')  {  
//empty means brackets unmatched
if (stk.isEmpty())   return false;
char charFromStack = ((Character)stk.pop()).charValue();
if (  ch == ']' && charFromStack != '[' 
 ||  (ch == ')' && charFromStack != '(')  )
 return false;
} // end if
} // end for loop
return stk.isEmpty();  //empty means matched,  else unmatched
}
}





3. EmptyStackException.java


public class EmptyStackException extends RuntimeException {
   public EmptyStackException()   {
      super ("The stack is empty.");
   }

   public EmptyStackException (String message)   {
      super (message);
   }
}



4. Main.java


class Main {
  public static void main(String args[])   {
      if (args.length < 1) {
      System.out.println("Usage: java Postfix [infix __EXPRESSION__]\n");
      return;
    }
    String infixExp = args[0];    
Calc c = new Calc();
if (!c.bracketsBalance(infixExp)){
System.out.println("괄호를 정확히 하세요~");
System.exit(1);
}
    String postfixExp = c.postfix(infixExp);
    Double result = c.result(postfixExp);
    System.out.println("The postfix __EXPRESSION__ for "+ infixExp +" is " + postfixExp);
    System.out.println("The result = "+result);
  }
}


댓글 없음:

댓글 쓰기