연쇄수식 계산기

2024. 4. 23. 15:53개발자 과정/Kotlin

https://n-pureun.tistory.com/136

 

계산식 문자열 필터링, 파싱

https://n-pureun.tistory.com/135 계산기 만들기 추상화 클래스를 통해 계산기를 만들라는 과제를 받았다. 계산기는 지독하리 만치 만든바... c언어 배울시절... ???: 변수와 연산자 배웠죠? 계산기 만들어

n-pureun.tistory.com

해당 글에서 이어진다.

 

문자열이 적절히 파싱가능한 상태라, 연쇄수식알고리즘은 그냥 아무거나 적용만 시켜도 음수와 소수처리까지 가능해진다. 

 

우선순위 세팅

private fun getPriority(operator: String): Int =
   when (operator) {
      "(", ")" -> 0
      "+", "-" -> 1
      "*", "/", "%" -> 2
      else -> -1
   }

= 후위 연쇄수식 변환에 맞게끔 우선순위를 세팅한다. 

 

후위연산 변환 함수

private val operators = listOf('(', ')', '+', '-', '*', '/', '%')
fun convertCalc(inputString: String) {
   val stack = Stack<String>()
   val postfix = mutableListOf<String>()
   inputString.split(" ").forEach { token ->
      if (token.isNotBlank()) {
         if (token in operators.toString()) {
            if (token == "(") stack.add(token)
            else if (token == ")") {
               while (stack.isNotEmpty()) {
                  val op = stack.pop()
                  if (op == "(") break
                  else postfix.add(op)
               }
            } else {
               while (stack.isNotEmpty()) {
                  if (getPriority(token) <= getPriority(stack.peek()))
                     postfix.add(stack.pop())
                  else break
               }
               stack.add(token)
            }
         } else postfix.add(token)
      }
   }
   while (stack.isNotEmpty()) postfix.add(stack.pop())
   println("후위연산 변환: $postfix")
   postfixCalc(postfix)
}

 

1. 먼저, 주어진 문자열공백을 기준으로 토큰분할

2. 분할된 토큰을 하나씩 처리하면서, 연산자인지 확인. 연산자일 경우 스택을 이용하여 후위 표기법으로 변환

   2-1. "("인 경우에는 스택에 저장

   2-2. ")"인 경우에는 "("를 만날 때까지 스택에서 원소를 꺼내 postfix에 추가

   2-3. 그 외의 연산자인 경우, 스택연산자와 비교하여 우선순위가 높은 연산자postfix에 추가

3. 모든 토큰을 처리한 후에 스택에 남아있는 원소postfix에 추가

4. 마지막으로 후위 표기법으로 변환된 수식을 출력후, 후위 표기법을 계산하는 함수를 호출

 

후위연산 계산 함수

private fun postfixCalc(postfix: List<String>) {
   val stack = mutableListOf<Double>()
   try {
      for (token in postfix) {
         when (token) {
            in operators.toString() -> {
               if(stack.size<2) throw Error("올바르지 않은 입력입니다.")
               val right = stack.removeAt(stack.lastIndex)
               val left = stack.removeAt(stack.lastIndex)
               val result = when (token) {
                  "+" -> Summation(left, right).calculateResult()
                  "-" -> Subtraction(left, right).calculateResult()
                  "*" -> Multiplication(left, right).calculateResult()
                  "/" -> Division(left, right).calculateResult()
                  "%" -> Remainder(left, right).calculateResult()
                  else -> throw Error("잘못된 문자가 있습니다.")
               }
               stack.add(result)
            }

            else -> {
               stack.add(token.toDouble())
            }
         }
      }
      if (stack.size != 1) {
         throw Error("잘못된 계산입니다.")
      }
      if(stack.first().isNaN()||stack.first().isInfinite()) throw Error("올바르지 않은 식입니다.")
      println("계산 결과: ${stack.first()}")
   } catch (e: Error) {
      println(e.message)
   }
}

 

1. 후위 표기법으로 변환되며 토큰화수식을 하나씩 처리

2. 토큰이 연산자인 경우, 스택에서 숫자를 꺼내 해당 연산자로 계산한 결과를 다시 스택에 저장

3. 토큰이 숫자인 경우, 해당 값스택에 저장

4. 모든 토큰을 처리한 후에 스택에 남아있는 값이 최종 계산 결과. 해당 결과를 출력

 

결과

= 이전 로직들과 맞물려 예외적 상황에서도 기능을 유지한다.

'개발자 과정 > Kotlin' 카테고리의 다른 글

(코딩테스트) 숫자 짝꿍 (StringBuilder)  (0) 2024.04.25
계산기 프로젝트 최종  (0) 2024.04.24
계산식 문자열 필터링, 파싱  (0) 2024.04.23
계산기 만들기  (0) 2024.04.22
(코딩테스트) 기사단원  (0) 2024.04.17