계산기 클래스를 만드는 여정

김태영·2024년 6월 5일
0

TIL

목록 보기
23/70
post-thumbnail

오늘 공부한 것

- 알고리즘 공원 산책 풀이
- Kotlin 문법 종합반 4주차 1챕터 ~ 5챕터
- 개인 과제: 계산기 만들기 Lv2

계산기 기능 구현하기

[ 조건 ]

  • Calculator 클래스를 이용한 사칙연산 및 출력
  • 출력 이후 추가적인 연산을 할 수 있게 구현
  • 나머지 연산자 추가하기
    • -1을 입력할 때까지 계산 반복하기
    • (1번 +, 2번 -, 3번 *, 4번 /, 5번 %)

사실 조건이 잘 이해가 가지 않아서, 모든 조건을 다 지켜야겠다! 보다는 이해한 만큼만 만들어보기로 했다.

  • Calculator 클래스에서는 계산과 그 결과를 출력해야 한다.
  • 계산을 하는 함수들은 추가적으로 연산을 할 수 있게 계산 결과를 반환해야 한다.
  • 나머지 연산의 경우, 계속해서 연산 유형을 입력 받아 계산을 반복할 수 있다.
    • -1을 입력 받으면, 계산기를 종료한다.

나는 이렇게 이해했다. 우선 필요한 것은, Calculator 클래스와 main 함수가 되겠다.

1. 사용자 입력

a. 계산할 숫자 입력

우선 기본적으로 사용자로부터 정수형 2개를 입력받아야 한다. 그러나 사용자가 정수형인 숫자만을 입력할 것이라는 보장이 없기 때문에 예외 처리를 해줘야 한다. 또한, 알맞은 숫자를 입력할 때까지 반복적으로 입력을 받아야 한다.

처음 한 번은 무조건 입력을 받아야 하기 때문에, 반복을 할 때 do ~ while 문을 사용하기로 했다. 그러나 구현하다 보니, 반복하는 조건을 true로 설정하게 되어서 그냥 while 문과 다를 게 없어졌다. 그래서 결국 while 문을 사용하는 쪽으로 수정했다.

예외 처리에는 try ~ catch 문을 사용했다.

fun getNumberInput(): List<Int> {
	println("계산하실 숫자를 공백으로 구분하여 입력해주세요.")

    while (true) {
        readlnOrNull()?.let {
            try {
		        // 공백으로 구분 된 문자열들을 모두 정수형으로 바꿔주기
                return it.split(" ").map(String::toInt)
            } catch (e: NumberFormatException) {
		        // 정수형으로 바꾸지 못한다면 이쪽으로 온다.
                println("올바른 값을 입력해주세요.")
            }
        }
    }
}

만약 사용자가 숫자만 입력했다면, 입력한 숫자들로 이루어진 List를 반환하는 형태로 구현해보았다. 원래 숫자 2개만 입력 받으려고 했는데, 호옥시 나중에 여러 숫자에 대한 계산도 추가하게 되는 상황을 고려해 리스트로 다루기로 했다.

b. 계산 방식 입력

사용자 입력을 처리하는 김에 계산 방식을 입력 받는 함수도 같이 구현했다. 똑같이 while 문으로 사용자가 올바른 값을 입력할 때까지 반복해주었고, try ~ catch 로 예외 처리를 해주었다.

private fun getType(): Int {
    println(
        """계산을 선택해주세요. 그만하려면 -1을 입력해주세요.
            |1. 더하기 2. 빼기 3. 곱하기 4. 나누기 5. 나머지 구하기 """.trimMargin()
    )

    while (true) {
        readlnOrNull()?.let {
			try {
				return it.toInt()
		    } catch (e: NumberFormatException) {
				println("숫자를 입력해주세요.")
		    }
        }
    } 
}

2. 연산 구현

더하기, 빼기, 나누기 같은 연산을 해주는 함수를 Calculator 클래스 내부에 작성해주었다. 연산을 하고, 결과를 출력하고, 연산 결과를 반환한다.

덧셈의 경우는 그냥 sum() 을 사용했지만, 다른 연산들은 reducefold를 사용해 구현했다.

fun add(numbers: List<Int>): Int {
    val result = numbers.sum()
    println("덧셈 결과 >> $result")
    return result
}

3. 반복 계산 구현 (나머지 연산)

나머지 연산의 경우, 계속해서 연산 유형을 입력 받아 계산을 반복할 수 있다. (-1은 종료)

나는 이 기능을 처음 사용자가 10과 52라는 숫자를 입력하고 나머지 연산을 수행하면, 그 결과를 출력한 뒤, 10과 52로 다시 하고 싶은 연산을 선택하는 기능으로 이해했다.

a. 연산 반복 함수

구현하기 위해 먼저 when 문을 사용해 입력 받은 숫자에 따라 해당하는 연산이 실행되도록 하였다. 1번에서 만들어둔 계산 방식 입력 함수(getType)를 사용하여 쉽게 구현할 수 있었다.

나머지 연산에만 쓰이는데 함수로 따로 뺀 이유는, 나머지를 구하는 함수는 딱 나머지 연산만 하게끔 구현하고 싶었다. 다른 add나 sub는 세 줄로 끝나는데 remain만 길어지면 좀.. 이상해 보일 것 같았다.

private fun loopCalculate(x: Int, y: Int) {
    while (true) {
        val type = getType()

        when (type) {
            -1 -> return println("종료합니다.")
            1 -> add(x, y)
            2 -> sub(x, y)
            3 -> mul(x, y)
            4 -> divide(x, y)
            5 -> remain(x, y)
            else -> println("올바른 값을 입력해주세요.")
        }
    } 
}

b. 나머지 연산과 연결

반복적인 연산을 할 수 있게 해주는 함수를 만들었으니, 이제 나머지를 구하는 함수와 연결만 해주면 된다. 쉬울 줄 알았지만 생각보다 까다로웠다.

  • 나머지 함수는 결과 값을 반환해야 한다.
  • 결과 값을 반환한 후, 연산을 반복 해야 한다.
  • 나머지 함수에 연산 반복 코드를 추가하면, 나머지 연산 후 또 나머지 연산을 했을 때 반복이 여러번 중첩되게 된다.
    • loop 함수가 loop 함수를 호출
    • ex. 10과 52 입력 → 나머지 연산 → 추가 연산으로 다시 나머지 연산 선택 을 하게 되면 반복이 중첩되어 -1을 2번 입력해야 계산기를 종료할 수 있다.

먼저 값을 반환한 후에 반복 연산 함수를 호출하기 위해 also 를 사용했다. 반복 중첩의 경우는 isLoop 라는 매개변수를 추가해 true 일 때만 반복 연산 함수를 호출하도록 했다.

fun remain(x: Int, y: Int, isLoop: Boolean = true): Int {
    var result = 0
    
    // 나눗셈과 같이 y가 0인 경우 "0으로 나눌 수 없습니다"라는 에러 방지
    try {
        result = x % y
        println("나머지 결과 >> $result")
    } catch (e: ArithmeticException) {
        result = -1
        println("0으로는 나눌 수 없습니다.")
    } finally {
    	// 에러가 발생했던 안했던 실행된다. 
        return result.also { if (isLoop) loopCalculate(x, y) }
    }
}

private fun loopCalculate(x: Int, y: Int) {
    while (true) {
        val type = getType()

        when (type) {
            ...
            // 반복 함수 내부에서 호출할 때는 loopCalculate()를 호출하지 않게 해주자!
            5 -> remain(x, y, false)
            else -> println("올바른 값을 입력해주세요.")
        }
    }
}

위와 같이 Calculator 클래스에 작성해주고 main 함수에서 사용해보면 잘 작동하는 것을 확인할 수 있다.

fun main() {
	val calculator = Calculator()
		
	// remain 함수의 결과값을 remainRes에 저장한다.
	// isLoop의 기본값이 true이기 때문에 loopCalculate 함수가 호출된다.
	val remainRes = calculator.remain(1, 2)
		
	// 사용자가 -1을 입력해 계산기를 종료하면 출력된다.
	println(remainRes) // 1 % 2 -> 1
}

잠깐!

return 문이 finally 블록 안에 있다는 경고가 뜬다. 작동에 문제는 없지만, 노란색도 물론 좋아하지만, 경고는 없을 수록 좋기 때문에 finally 블록을 없애고 try ~ catch 문 바깥에 작성해주었다.

마치며

강의에서 배운 예외 처리를 사용할 수 있는 경험이었다. 사용법이 크게 어렵지 않아 다행이다. 오히려 헤맸던 건, 어떻게 결과 값을 반환하면서 다른 함수를 호출하지? 였던 것 같다. 그래도 방법이 있어서, 심지어 프로그래머스에서 많이 봤었던 also여서 다행이었다.

사실 클래스를 나누고, 상속 받고, 역할 분담하고 이런 일에 익숙하지 않아 감이 잘 안잡혔다. 심지어 과제의 조건도 이해가 어려웠다. 다행히도 부트캠프 사람들에게 물어물어 내가 이해한 게 맞는 건지 틀린 건지 어느 정도 윤곽이 잡히게 되었고, 역시 개발은 같이 하는 사람이 필요하구나! 라는 것을 알게되었다. 좋은 사람들을 만나게 되어 행복하다. 나도 다른 사람들에게 좋은 사람으로 기억될 수 있게 노력할 것이다.

profile
화이팅

0개의 댓글