개발하는 뚝딱이

[iOS] 계산기 만들기 D+3 본문

iOS 개발일지

[iOS] 계산기 만들기 D+3

개발자뚝딱이 2020. 3. 10. 20:16

iOS 계산기 만들기 개발 3일차

 

 


깃 허브 저장소 주소 ⤦

https://github.com/TheSongOfSongs/Calculator

 

 

 

구현 목표

괄호가 있는 식의 구현

 

 

예상 시나리오

- 괄호가 포함된 수식을 포함하기 위해선, 사용자가 수식을 확인하며 입력을 해야만 한다.

→ 라벨을 통해 현재 작성 중인 수식을 화면에 보여준다.

 

- 기존의 피연산자 두 개만으로 계산하던 것과 달리 우선순위가 정해져 있는 수식의 작성이 가능해졌다. 혼합 계산을 구현해야 한다.

→ 연산자마다 우선순위를 부여하여 먼저 계산할 순서를 정한다.

 

 

 

구현 내용

수식 라벨 추가, 수식 배열 추가

    @IBOutlet var history: UILabel!
    
    var expressionArray: [String] = []

수식을 화면에 출력할 라벨과, 수식을 저장할 배열을 선언하였다. 단순히 지난 계산 결과값과 현재 입력한 값의 연산을 통해 결과를 얻어내면, 우선순위가 먼저인 값들을 계산할 수 없다. 따라서 수식이 완성된 후 계산하기 위하여 우선 식을 저장할 필요가 있다. 연산자와 피연산자를 한꺼번에 저장하기 위해 String 타입으로 지정하였으며 초기값은 비어있다.

 

 

괄호 버튼을 눌렀을 때

    @IBAction func braketFirst(_ sender: Any) {
        print(expressionArray)
        whenSelectedOperator(6)
    }
    
    @IBAction func bracketSecond(_ sender: Any) {
        expressionArray.append(String(printNum))
        expressionArray.append(")")
    }
    
       
    func whenSelectedOperator(_ num:Int) {
        switch num {
        case 1:
            expressionArray.append(String(removePoint(num: printNum)))
            expressionArray.append("+")
            
        case 2:
            expressionArray.append(String(removePoint(num: printNum)))
            expressionArray.append("-")
            
        case 6:
        	expressionArray.append("(")
            
        default:
            break
        }
        self.history.text = expressionArray.joined(separator: " ")
        operation = num
        tempNum = printNum
        
        printNum = 0
        howManyInit()
    }

괄호 버튼을 눌렀을 때, 함수 whenSelectedOperator(_ num: Int)를 호출하였다. whenSelectedOperator(_num: Int)는 연산자 버튼을 클릭했을 때 호출되는 함수이며 expressionArray 배열의 마지막에 괄호를 추가하도록 하였다. 또한 이 함수에서 수식을 출력하는 라벨 또한 동시에 바뀔 수 있도록 구현하였다.

배열을 추가함에 따라 기존의 코드 또한 수정해야 했다. 연산자 버튼을 클릭 시 원래는 각 케이스에 따라 바로 연산을 하도록 구현했으나, 현재는 우선 배열에 현재 값과 연산자를 저장하도록 하였다. case 1과 case 2처럼 case 5까지 같은 형태로 구현하였다. 그러나 case 6에서는 현재 값을 배열에 저장하도록 구현하면, 괄호 앞에 0이 들어간다. 이미 배열에 저장하였기 때문에 현재 값은 0으로 할당되고 그 값이 배열에 저장되기 때문이다.

 

 

계산버튼을 눌렀을 때

@IBAction func printResult(_ sender: Any) {
        if expressionArray.last != ")" {
            expressionArray.append(String(removePoint(num: printNum)))
        }

        self.history.text = expressionArray.joined(separator: " ")
        expressionArray.append("\n")
        
        getBracketValue(num: -1)
        getOtherValue(num: -1, lastCh: "\n")
        getAddSubValue(num: -1, lastCh: "\n")
        
        operation = 0
        howManyInit()
        printNum = Double(expressionArray.first!)!
        self.result.text = removePoint(num: (Double(expressionArray.first!)!))
        expressionArray.removeAll()
    }
    
    func getBracketValue(num: Int) {
        var i = num
        repeat {
            i += 1
            switch expressionArray[i] {
            case "(" :
                if !expressionArray.contains(")") {
                    expressionArray.append(")")
                }
                getOtherValue(num: i + 1, lastCh: ")")
                getAddSubValue(num: i + 1, lastCh: ")")
                expressionArray.remove(at: i)
                expressionArray.remove(at: i+1)
            default:
                break
            }
        } while expressionArray[i] != "\n"
    }
    
    func getOtherValue(num: Int, lastCh: String) {
        var i = num
        var insertNum: Double = 0
        
        repeat {
            i += 1
            switch expressionArray[i] {
            case "*" :
                insertNum = operateTwoNum(Double(expressionArray[i-1])!, Double(expressionArray[i+1])!, operation: operateMultiply)
                for _ in 0..<3 { expressionArray.remove(at: i-1) }
                expressionArray.insert(String(insertNum), at: i-1)
                i -= 1
            default:
                break
            }
        } while expressionArray[i] != lastCh
    }
    

    func getAddSubValue(num: Int, lastCh: String) {
        var i = num
        var insertNum: Double = 0

        repeat {
            i += 1
            switch expressionArray[i] {
            case "+" :
                insertNum = operateTwoNum(Double(expressionArray[i-1])!, Double(expressionArray[i+1])!, operation: operateAdd)
                for _ in 0..<3 { expressionArray.remove(at: i-1) }
                expressionArray.insert(String(insertNum), at: i-1)
                i -= 1
            default:
                break
            }
        } while expressionArray[i] != lastCh
        
    }

이 부분을 구현할 때 가장 많은 생각을 하였다. 완성된 수식을 구하였고, 이제 우선순위에 따라 배열에서 계산을 하면 된다. 우선순위는 괄호 > 곱셈, 나눗셈, 나머지 > 덧셈, 뺄셈 순으로 정하였다. 다시 괄호 안에서 이 우선 순위에 따라 계산하도록 만들었다. 재귀가 생각이 나여 이를 구현하기 위해 함수 단위로 쪼개어 getBracketValue, getOtherValue, getAddSubValue로 나누었다. '=' 버튼을 클릭 시 함수들을 차례로 실행하여 곱셈, 나눗셈, 나머지를 계산할 땐 괄호 계산이 이미 끝났도록 하였고, 덧셈과 뺄셈을 할 땐 이미 상위 우선순위의 연산은 종료되었도록 구현하였다.

함수를 재사용할 수 있도록 기존의 수식 맨 끝에 \n을 추가하였고 \n을 만나면 수식의 끝을 알 수 있도록 하였다. 그러나 '(' 괄호를 만나면 수식의 끝까지 확인하는 것이 아니라 ')'을 만날 때까지 수식을 확인해야 한다. 그래서 이 부분은 파라미터로 주어 '\n' 혹은 ')'까지인지 경우에 따라 바꿔줄 수 있다.

 

 

리팩토링

   func howManyInit() {
        howManyDecimal = 0
        howManyFraction = 0
    }

위 함수에 포함된 두 줄이 여러 함수에 걸쳐 3번 반복적으로 등장한다. 그래서 간단하지만 함수로 분류해내었다.

 

+ 이 프로젝트의 코드에서 switch문이 4번 등장한다. 각 switch문에서 반복되는 문장이 많은데 이를 효율적으로 분류하고 싶다.

 

보완해야 할 점

사용자가 괄호 앞에 연산자를 붙이지 않고 숫자만 입력할 경우, 그 숫자는 무시된다. 따라서 괄호 앞에 연산자를 정해주지 않으면 '*'가 default로 수식에 들어갈 수 있도록 해줘야 한다.

또한 사소한 점들을 고칠 수 있도록 계속 테스팅해야 할 것이다.

 

 

결과물

 

'iOS 개발일지' 카테고리의 다른 글

[iOS] 계산기만들기 D+5  (0) 2020.03.26
[iOS] 환율계산기1  (0) 2020.03.24
[iOS] 계산기만들기 D+4  (0) 2020.03.12
[ios] 계산기 만들기 D+2  (0) 2020.03.06
[iOS] 계산기 만들기 D+1  (0) 2020.03.05