코틀린 개인 과제

수식입력이 가능한 계산기 만들기

songyooho 2024. 6. 4. 16:35

1. 설계 및 코드

1)연산 클래스

-연산클래스를 추상화한 AbstractOperation 클래스를 만들은 뒤 AddOperation(더하기), SubstractOperation(빼기), MultiplyOperation(곱하기), DivideOperation(나누기) 클래스들을 만들어 AbstractOperation을 상속받고 각 연산 메소드를 오버라이드 하도록 함.

open class AbstractOperation() {
    open fun operation(num1:Int, num2:Int):Int{return 0}
}
class AddOperation():AbstractOperation() {
    override fun operation(num1:Int, num2:Int):Int {
        return num1 + num2
    }
}

2) Calculator 클래스

<1>postfix: 입력받은 수식 String을 연산자와 피연산자 단위로 분해하고 infix형태의 순서를 postfix형태로 변환하는 메소드. 스택을 이용

fun postfix(exp:String):Array<Any>{
        val stack= Stack<String>()
        var arr= arrayOf<Any>()

        var order=mapOf<String,Int>("(" to 0,"+" to 1, "-" to 1, "*" to 2, "/" to 2, "%" to 2)

        //수식을 피연산자와 연산자들의 배열로 변환
        val oplist=arrayOf<String>("+","-","*","/","%","(",")")
        val expArr=exp.toCharArray()
        var explist= arrayOf<String>()
        var tmp=StringBuilder()
        for(i in expArr){
            val tmpi=i.toString()
            if(oplist.contains(tmpi)){
                if(!tmp.isEmpty()){
                    explist+=tmp.toString()
                    tmp.clear()
                }
                explist+=tmpi
            }
            else{
                tmp.append(i)
            }
        }
        //마지막 남은 숫자들 있으면 추가
        if(!tmp.isEmpty()){
            explist+=tmp.toString()
        }



        for(i in explist){
            if(oplist.contains(i)){
                if(stack.empty()){
                    stack.push(i)
                }else if(i=="("){
                    stack.push(i)
                }
                else if(i==")"){
                    while(stack.peek()!="("){
                        arr+=stack.pop()
                    }
                    stack.pop()
                }
                else if(order[stack.peek()]!!>=order[i]!!){
                    while (order[stack.peek()]!!>=order[i]!!){
                        arr+=stack.pop()
                        if(stack.isEmpty()){
                            break
                        }
                    }
                    stack.push(i)
                }
                else{
                    stack.push(i)
                }
            }
            else{
                arr+=i.toInt()
            }
        }
        while(!stack.empty()){
            arr+=stack.pop()
        }
        return arr
    }

-규칙(결과는 배열)

[1] 숫자는 바로 배열로 넘겨줌

[2] 연산자의 경우

-스택이 비어있으면 스택에 넣음

-스택이 비어있지 않은 경우 아닌경우

  • 여는 괄호인 경우 일단 스택에 넣음
  • 닫는 괄호인 경우 여는 괄호가 나올때까지 스택의 원소들을 차례로 pop하여 배열에 넣어줌. 괄호는 배열에 넣지 않음
  • 괄호가 아닌경우:
    • 스택 맨위의 연산자가 현재 연산자 보다 우선순위가 높거나 같은 경우: 스택내에 현재 연산자 이상의 우선순위를 가진 연산자가 남아있지 않을때 까지 pop해서 배열로 옮긴다. 이후 스택에 현재 연산자를 push함
    • 아닌경우 바로 현재 연산자를 push

[3]for을 다 돈 뒤 스택에 남아있는 연산자들은 모두 pop해서 배열로 넘김

[4]최종적으로 배열이 postfix형태 수식이 된다.

 

<2>calculation:postfix형태의 수식배열을 이용하여 결과값 도출. 

-postfix 메소드를 이용하여 문자열 형태 중위표현식을 배열로 토큰화된 형태의 후위표현식으로 바꿔줌.

-스택을 이용하여 계산:숫자는 계속 스택에 넣다가 연산자가 나오면 스택 맨 위의 두 숫자를 pop해 연산후 스택에 다시 넣기 반복

fun calculation(s:String):Int{
        var stack= Stack<Int>()
        val arg=postfix(s)

        for(i in arg){
            if(i is Int){
                stack.push(i)
            }
            else{
                val post=stack.pop()
                val pre=stack.pop()
                stack.push(operation(pre,post, i as String))
            }
        }
        return stack.pop()
    }

 

<3>Calculator 전체 코드

package com.example.calculator

import java.util.*

class Calculator() {
    val add=AddOperation()
    val sub=SubstractOperation()
    val mul=MultiplyOperation()
    val div=DivideOperation()

    fun operation(num1:Int,num2:Int,op:String):Int{
        if(op=="%"){
            return num1%num2
        }
        else{
            val obj=when(op){
                "+" ->add
                "-" ->sub
                "*" ->mul
                else -> div
            }
            return obj.operation(num1, num2)
        }
    }

    fun postfix(exp:String):Array<Any>{
        val stack= Stack<String>()
        var arr= arrayOf<Any>()

        var order=mapOf<String,Int>("(" to 0,"+" to 1, "-" to 1, "*" to 2, "/" to 2, "%" to 2)

        //수식을 피연산자와 연산자들의 배열로 변환
        val oplist=arrayOf<String>("+","-","*","/","%","(",")")
        val expArr=exp.toCharArray()
        var explist= arrayOf<String>()
        var tmp=StringBuilder()
        for(i in expArr){
            val tmpi=i.toString()
            if(oplist.contains(tmpi)){
                if(!tmp.isEmpty()){
                    explist+=tmp.toString()
                    tmp.clear()
                }
                explist+=tmpi
            }
            else{
                tmp.append(i)
            }
        }
        //마지막 남은 숫자들 있으면 추가
        if(!tmp.isEmpty()){
            explist+=tmp.toString()
        }



        for(i in explist){
            if(oplist.contains(i)){
                if(stack.empty()){
                    stack.push(i)
                }else if(i=="("){
                    stack.push(i)
                }
                else if(i==")"){
                    while(stack.peek()!="("){
                        arr+=stack.pop()
                    }
                    stack.pop()
                }
                else if(order[stack.peek()]!!>=order[i]!!){
                    while (order[stack.peek()]!!>=order[i]!!){
                        arr+=stack.pop()
                        if(stack.isEmpty()){
                            break
                        }
                    }
                    stack.push(i)
                }
                else{
                    stack.push(i)
                }
            }
            else{
                arr+=i.toInt()
            }
        }
        while(!stack.empty()){
            arr+=stack.pop()
        }
        println(arr.contentToString())
        return arr
    }

    fun calculation(s:String):Int{
        var stack= Stack<Int>()
        val arg=postfix(s)

        for(i in arg){
            if(i is Int){
                stack.push(i)
            }
            else{
                val post=stack.pop()
                val pre=stack.pop()
                stack.push(operation(pre,post, i as String))
            }
        }
        return stack.pop()
    }


}

 

3) Main함수

fun main(){
    while(true){
        println("수식을 입력해 주세요(종료하려면 -1입력)")
        var exp= readLine().toString()
        if(exp=="-1"){
            break
        }
        val calculator=Calculator()
        println("결과: ${calculator.calculation(exp)}")
    }
    println("종료되었습니다")
}

 

2. 실행 결과

 

3. 느낀점

추가로 넣은 기능인 수식 입력 및 계산이 조금 까다로웠다. infix를 postfix로 바꾸는 방법을 명확히 적어둔 곳이 없어서 여러군데를 확인하며 설계하는것이 번거로웠지만 그 외에는 수월하게 만들 수 있었던것 같다.

'코틀린 개인 과제' 카테고리의 다른 글

회원가입 MVVM 과제  (0) 2024.07.14
챌린지반 3주차 첫번째 과제: 디자인 패턴 구현  (0) 2024.07.09
로그인 앱 제작-2  (0) 2024.06.25
로그인 앱 제작  (0) 2024.06.19
키오스크 제작  (0) 2024.06.13