조건) AbstractOperation라는 추상 클래스를 생성하여 AddOperation(더하기), SubstractOperation(빼기), MultiplyOperation(곱하기), DivideOperation(나누기) 연산 클래스 정의
계산기의 수행 절차를 생각해보자.
-> 매우 간단해보였다...
조건은 간단하게 수행할 수 있었다.
interface AbstractOperation {
fun doOperation(a: Float, b: Float): Float
}
class AddOperation: AbstractOperation {
override fun doOperation(a: Float, b: Float): Float = a + b
}
class SubtractOperation: AbstractOperation {
override fun doOperation(a: Float, b: Float): Float = a - b
}
class MultiplyOperation: AbstractOperation {
override fun doOperation(a: Float, b: Float): Float = a * b
}
class DivideOperation: AbstractOperation {
override fun doOperation(a: Float, b: Float): Float = a/b + a%b
}
fun main() {
val calculator = Calculator()
val br = BufferedReader(InputStreamReader(System.`in`))
val input = br.readLine().replace(" ", "")
val result = calculator.doIt(input)
println(result)
}
Calculator.doCalculate(str: String)을 정의하였으며 숫자만 모아놓은 numbers와 연산자들만 모아놓은 operators를 생성하였다. 반복문과 when을 이용하여 앞에서부터 +, -, *, %을 수행하게 하였다. 하지만 계산 시 우린 *와 %를 우선 수행한다.
private fun doCalculate(str: String): Float {
//정규표현식을 이용하여 input값인 str에서 숫자만 저장한다.
val numbers = ArrayList(str
.replace("[^₩.0-9]".toRegex(), " ")
.split(" ")
.map {
it.toFloat()
}
)
//반대로 이용하여 operator만 저장한다.
val operators = str.replace("[₩.0-9]".toRegex(), " ").split(" ").filter {
it.isNotEmpty()
}
//곱셈, 나눗셈 먼저 계산
operators.forEachIndexed { index, it ->
if (it == "*") {
val n = multiply.doOperation(numbers[index], numbers[index+1])
numbers[index] = n
numbers[index + 1] = -1F
} else if(it == "%") {
val n = divide.doOperation(numbers[index], numbers[index+1])
numbers[index] = n
numbers[index + 1] = -1F
}
}
//계산 결과 값인 result에 -,+ 연산들을 하여 더한다.
var result = numbers.first()
for((index, ele) in numbers.drop(1).withIndex()) {
if(ele == -1F) continue
result = when(operators[index]) {
"+" -> add.doOperation(result, ele)
"-" -> subtract.doOperation(result, ele)
else -> throw Exception("* and % is not filtered.")
}
}
return result
}
잘 작동한다.
큰 문제였다... 한 4-5시간 걸린듯하다.
-> 재귀 함수를 이용하기로 하였다.
우선 재귀 함수를 이용하기 위해 '기본 수행 단위'와 '탈출 조건'을 정의해보자
우선 기본 수행 단위는 위 doCalculate를 이용하면 되겠다.
탈출 조건은 if에서 ()의 유무를 확인하면 쉽다.
그러면 함수를 정의해보자.
private fun containsBracket(str: String): Boolean = str.contains("(")
private fun findBrackets(str: String): List<String> {
var numOfOpen = 0
var numOfClose = 0
val bracketList = mutableListOf<String>()
var firstOpenIndex = 0
str.forEachIndexed { i, ele ->
if(ele == '(') {
numOfOpen++
if(numOfOpen == 1) firstOpenIndex = i
}
else if(ele == ')') {
numOfClose++
if(numOfOpen == numOfClose) {
bracketList.add(str.substring(firstOpenIndex, i+1))
numOfClose = 0
numOfOpen = 0
}
}
}
return bracketList
}
fun doIt(str: String): Float {
return if(!containsBracket(str)) {
doCalculate(str)
} else {
val brackets = findBrackets(str)
var result = str
for(ele in brackets) {
var removedBracket = ele.substring(1, ele.length-1)
result = result.replace(
ele,
doIt(removedBracket).toString()
)
}
return doIt(result)
}
}
if문으로 () 포함 유무를 확인하여 탈출한다.
괄호 내부 수식을 재귀 처리한다. 그리고 그 값을 "{수식}" 과 replace 한다.
잘 작동한다.
복잡하게 생각한거에 비해 코드는 짧게 나온거 같다... 어렵다.
findBrackets 함수와 doCalculate 함수들은 좀 더 간결하게 작성할 수 있을거 같다. 나중에 고쳐보자.