글 작성자: HEROHJK

자료구조들이 구현 되었으니, 이제 스택을 이용하여 식을 계산하는 함수를 구현하는일이 남았습니다.


우선 간략한 순서는 다음과 같습니다.


1. 중위식을 전용 데이터 배열로 변환


2. 중위식으로 이루어진 전용 데이터 배열을 후위식으로 이루어진 전용 데이터배열로 변환


3. 후위식으로 이루어진 데이터배열을 연산하여 결과를 추출



이 포스트에서는 1. 중위식을 전용 데이터 배열로 변환 하는 부분을 다룹니다.


중위식으로 이루어진 문자열을, 이전 포스트에서 작성하며 설명한 계산기 전용 데이터로 변환하여 배열에 담는과정인데, 설명드리자면 순서는 다음과 같습니다.


1. 문자열 전체를 처음부터 순회합니다

1. 해당 위치의 문자의 타입을 판별합니다

2. 문자의 타입이 마이너스 기호일 경우에는 유효성 검사를 통해서 음수표시인지, 연산자기호인지를 판별합니다

1. 음수표시라면 숫자타입으로 값을 추출하여 반환할 데큐에 담습니다.

2. 연산자표시라면 음수연산자타입으로 값을 추출하여 반환할 데큐에 담습니다

3. 타입이 값이라면 숫자타입으로 값을 추출하여 반환할 데큐에 담습니다

4. 값이 연산자표시, 괄호표시라면 각 타입으로 값을 추출하여 반환할 데큐에 담습니다

5. 그외의 경우는 올일이 없겠지만, 생략하는걸로 간주하고 위치만 세어줍니다.


코드는 다음과 같습니다.


먼저 마이너스 기호에 관해 숫자 유효성을 판단하는 함수입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//숫자 유효성 검사. 해당 문자가 숫자인지, 음수표시의 -인지, 소수점인지를 판별한다.
func NumberValidation(_ index: Int, _ string: String-> Bool{
    //문자열 -> 문자배열로 변환
    let characters = Array(string)
    
    //먼저 숫자인지 판별, 숫자라면 참을 반환한다
    if DataTypeCheck(characters[index]) == CalculatorEnumeration.value{
        return true
    }
    
    //소수점인지 판별, 소수점이고, 앞에 값이 숫자라면 참을 반환한다
    if DataTypeCheck(characters[index]) == CalculatorEnumeration.dot{
        if DataTypeCheck(characters[index-1]) == CalculatorEnumeration.value{
            return true
        }
    }
    
    //음수표시인지 판별
    if DataTypeCheck(characters[index]) == CalculatorEnumeration.operatorMinus{
        //첫번째 자리라면 참을 반환
        if index == 0{
            return true
        }
        //앞의 값이 왼쪽괄호표시라면 참을 반환
        if DataTypeCheck(characters[index-1]) == CalculatorEnumeration.lParen{
            return  true
        }
        //앞의 값이 연산자 표시이고, 그 앞의 값이 숫자라면 참을 반환
        if DataTypeCheck(characters[index-1]) == CalculatorEnumeration.operatorMinus || DataTypeCheck(characters[index-1]) == CalculatorEnumeration.operatorPlus || DataTypeCheck(characters[index-1]) == CalculatorEnumeration.operatorMultiple || DataTypeCheck(characters[index-1]) == CalculatorEnumeration.operatorDivide{
            if DataTypeCheck(characters[index-2]) == CalculatorEnumeration.value{
                return true
            }
        }
    }
    
    //위의 조건들 외에는 무조건 거짓을 반환
    return false
}
cs


연산자, 괄호의 경우는 1글자라서 추출이 쉽습니다(배열[위치] -> 추출된 기호) 하지만 숫자라면 범위가 있으므로 이야기가 달라집니다.


그래서 숫자를 추출하는 함수를 구현했으며, 코드는 다음과 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//숫자 가져오기
func GetNumber(_ idx: Int, _ string: String-> (index: Int, value: String){
    
    //인덱스
    var index=idx
    //문자열->문자집합
    let characters = Array(string)
    //추출될 값
    var value: String=""
    
    //해당 문자가 숫자나 소수점일동안 반복한다
    while index<string.count && NumberValidation(index, string){
        value.append(characters[index])
        index=index+1
    }
    
    return (index, value)
}
cs


마지막으로, 맨위에 설명드린 중위식 문자열 -> 중위식 데이터 배열로 변환하는 코드는 다음과 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//전용 데이터타입으로 변환하기
func StringToCalculatorDataType(_ valueString: String-> Dequeue<CalculatorData>{
    //배열로 변환한다
    var valueArray=Array(valueString)
    var dequeue: Dequeue<CalculatorData> = Dequeue<CalculatorData>()
    let maxIndex=valueString.count
    var index=0
    var type: CalculatorEnumeration = .etc
    
    //문장의 끝까지 반복
    while index<maxIndex{
        //문자의 데이터타입을 체크한다
        type=DataTypeCheck(valueArray[index])
        //타입에 따른 분류
        switch type{
        case .operatorMinus :
            //-표시일 경우 판별 결과에 따라 연산자인지, 숫자인지 판별한다
            if NumberValidation(index, valueString) == true{
                //값일 경우 값을 추출해온 후 데큐에 담는다
                let newTuple = GetNumber(index,valueString)
                let newDataType: CalculatorData = CalculatorData(.value, newTuple.1)
                dequeue.Insert(newDataType)
                //인덱스를 센다
                index=newTuple.0
            }
            else{
                //연산자, 괄호일경우에만 데큐에 담는다
                var tmpString: String = ""
                tmpString.append(valueArray[index])
                let newDataType: CalculatorData = CalculatorData(type, tmpString)
                dequeue.Insert(newDataType)
                //인덱스를 센다
                index=index+1
            }
        case .value :
            //값일 경우 값을 추출해온 후 데큐에 담는다
            let newTuple = GetNumber(index,valueString)
            let newDataType: CalculatorData = CalculatorData(type, newTuple.1)
            dequeue.Insert(newDataType)
            //인덱스를 센다
            index=newTuple.0
            
        case .operatorDivide, .operatorMultiple, .operatorPlus, .lParen, .rParen :
            //연산자, 괄호일경우에만 데큐에 담는다
            var tmpString: String = ""
            tmpString.append(valueArray[index])
            let newDataType: CalculatorData = CalculatorData(type, tmpString)
            dequeue.Insert(newDataType)
            //인덱스를 센다
            index=index+1
            
        default:
            //그 외에는 인덱스만 센다
            index=index+1
            break;
        }
    }
    
    return dequeue
}
cs


이렇게 변환과정을 거쳐두면, 숫자, 연산자, 괄호로만 판별을 하면 되기때문에, 후위표기 변환, 계산 하기가 훨씬 쉬워집니다.


반응형