본문 바로가기

프로그래밍/비주얼베이직

비주얼베이직 계산기 만들기 코드 (후위 표기법)

728x90
반응형

 

비주얼베이직 수업 시간에 했던 후위 표기법을 이용한 사칙연산 계산기 과제이다

 

코딩 공부를 할 때 보통 if else 문을 이용한 계산기 만들기를 자주 할것인데

우리가 일반적으로 사용하는 계산기랑은 다른걸 그냥 봐도 알것이다

 

아래 방법은 우리가 일반적으로 사용하는 계산기를 만들어보는 방법이다

1 + 5 * 3 을 계산하려고 한다면 우리는 해당 수식을 바꿔야한다

 

이 때 배열과 스택을 사용하는데

배열에는 숫자, 스택에는 연산자를 넣는다고 생각하면 된다

 

예를 들어보자

 

1 + 5 * 3 - 2 라는 수식을 계산해야한다 우리는 연산자 우선순위에 의해서

곱하기를 먼저하고 그 다음 + 나 -를 하는 것을 알고 있다

 

그래서 다음과같은 규칙을 적용한다

수식을 문자열이라고 하자

문자열의 0번째 인덱스부터 읽기 시작하는데

1. 숫자면 배열에 추가한다.
2. 곱하기 또는 나누기면 스택에 추가한다.
3. 더하기 또는 빼기면 현재 스택에 담겨있는 연산자를 전부 pop 하여 순서대로 배열에 추가하고
    읽었던 연산자를 스택에 추가한다.
4. 문자열을 전부 읽었으면 스택에 남아있는 연산자를 전부 pop 하여 순서대로 배열에 추가한다.

 

차례대로 해보자

먼저 문자열 첫번째를 읽는다.

숫자다.

배열에 넣는다.

 

 

그다음 +다

더하기 연산자 기호이다

스택에 있는 모든걸 배열에 차례대로 넣고 +를 넣는다

그런데 스택에 아무것도 없다

그냥 넣는다

 

그다음 숫자다 배열에 넣는다

 

그 다음 곱하기 연산자다 스택에 넣는다

 

그 다음 숫자다 배열에 넣는다

 

그 다음 빼기 연산자 기호이다.

스택에 있는걸 차례대로 배열에 넣는다

 

 

 

빼기 까지 스택에 넣어주고 문자열의 다음 인덱스를 읽는다 숫자다

배열에 넣는다

근데 문자열의 마지막 인덱스였다

그러면 스택에 남아있는 연산자를 배열에 넣는다

 

그러면 만들어지는 문자열은 153*+2- 이게 후위 표기법이다

 

그러면 후위표기법으로 어떻게 연산을 하는가

 

 

후위 표기법으로 만들어진 문자열을 첫번째부터 읽기시작한다

그리고 다음 규칙을 적용한다

문자열을 읽다가 기호를 만나면 기호 바로 전 숫자와 그 전숫자를 기호에 해당하는 연산을 하고 그 전숫자의 인덱스에 결과를 저장하고 기호와 기호 앞 인덱스를 배열에서 삭제한다. 이걸 인덱스가 하나일때까지 반복한다.

말이 어렵다 그림으로 보자

 

숫자다 넘어간다

또 숫자다 넘어간다

 

또 숫자다 넘어간다

 

곱하기 기호이다

그러면 3과 5를 * 연산을 해준다 그리고 그걸 5가 있던 자리에 넣고 3과 *은 삭제한다 배열에서 통째로

메모리 주소를 사용하는 배열 기본 배열을 사용하려면 뒷원소들을 앞으로 당겨주는 연산이 필요하고

동적배열을 이용하면 삭제해주면 된다

 

그리고 다시 처음부터 읽기 시작한다

숫자다 넘어가자 

 

또 숫자다 넘어가자

더하기 기호이다 그러면 1과 15를 +로 연산해주고 또 삭제해주면된다

 

숫자다 넘어가자

 

 

숫자다 넘어가자

 

마이너스 기호이다 그러면 16과 2를 - 해주면된다 여기서 순서를 정확히 지켜야함

 

야호 배열의 길이는 1개 반복 끝! 그러면 결과는 14!

1 + 3 * 5 - 2 의 결과는? 14!!

 

만약 () 기호가 있을경우 ( 기호를 만나면 배열과 스택을 하나 더 만들어서 )가 나올때까지 새로만든 배열과 스택에 넣으면된다 )를 만나면 새로운 스택을 새로운 배열에 넣고 새로운 배열을 원래 배열에 넣으면 된다

 

비주얼 베이직으로 후위표기법 변환 코드를 작성하면 이렇게 된다

배우면서 적은거라 당연히 최적화는 제대로 되있지도 않지만 그냥 아 대충 이렇게 하는거구나 참고만 하면 좋을듯 합니다

 

 

연산을 하는 코드는 스위치 케이스문을 사용하던지 if else 를 사용하던지 자

 

 

 

 

 

https://github.com/wndudwkd003/va-calculate-example

 

GitHub - wndudwkd003/va-calculate-example

Contribute to wndudwkd003/va-calculate-example development by creating an account on GitHub.

github.com

 

Imports Accessibility

Public Class Form1
    Public Structure Calculator
        Dim numberArr As ArrayList
        Dim signStack As Stack
    End Structure

    Dim bkCalulators As New ArrayList
    Dim InputStringArr As New ArrayList
    Dim InputString As String = ""

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim calculatorInstance As New Calculator With {
            .numberArr = New ArrayList,
            .signStack = New Stack
        }
        bkCalulators.Add(calculatorInstance)
        Label_window.Text = InputString
    End Sub

    Private Sub Button_n_0_Click(sender As Object, e As EventArgs) Handles Button_n_0.Click
        setLabelWindowIS("0")
    End Sub

    Private Sub setLabelWindowIS(v As String)
        '입력 조건

        '최대 길이 제한
        If InputString.Length < 29 * 2 Then
            If InputString.Length = 0 And Not IsNumeric(v) Then
                '첫입력에 기호가 오면
                If v = "(" Then
                    InputString = InputString & v
                Else
                    Return
                End If

            ElseIf InputString.Length > 0 And Not IsNumeric(v) Then
                If Not v = "(" And Not v = ")" And Not InputString.Last = "(" And Not InputString.Last = ")" And Not IsNumeric(InputString.Last) Then
                    Dim tmp As String = ""
                    Dim i As Integer

                    '기호뒤에 기호가오면 맨끝 기호 대신 새로운 기호를 넣음
                    For i = 0 To InputString.Length - 2 Step 1
                        tmp = tmp & (InputString(i))
                    Next i

                    '기호 추가 전 입력된 문자열에 새로 입력된 기호 추가
                    InputString = tmp & v
                Else
                    '그냥 기호 입력
                    InputString = InputString & v
                End If

            Else
                '문제 없으면 문자열 뒤에 추가
                InputString = InputString & v
            End If

            Label_window.Text = InputString
        Else
            MsgBox("최대 58개만 입력할 수 있습니다. " + vbCrLf + "더이상 입력할 수 없습니다.", 0, "경고")
        End If
    End Sub

    Private Sub Button_n_1_Click(sender As Object, e As EventArgs) Handles Button_n_1.Click
        setLabelWindowIS("1")
    End Sub

    Private Sub Button_n_2_Click(sender As Object, e As EventArgs) Handles Button_n_2.Click
        setLabelWindowIS("2")
    End Sub

    Private Sub Button_n_3_Click(sender As Object, e As EventArgs) Handles Button_n_3.Click
        setLabelWindowIS("3")
    End Sub

    Private Sub Button_n_4_Click(sender As Object, e As EventArgs) Handles Button_n_4.Click
        setLabelWindowIS("4")
    End Sub

    Private Sub Button_n_5_Click(sender As Object, e As EventArgs) Handles Button_n_5.Click
        setLabelWindowIS("5")
    End Sub

    Private Sub Button_n_6_Click(sender As Object, e As EventArgs) Handles Button_n_6.Click
        setLabelWindowIS("6")
    End Sub

    Private Sub Button_n_7_Click(sender As Object, e As EventArgs) Handles Button_n_7.Click
        setLabelWindowIS("7")
    End Sub

    Private Sub Button_n_8_Click(sender As Object, e As EventArgs) Handles Button_n_8.Click
        setLabelWindowIS("8")
    End Sub

    Private Sub Button_n_9_Click(sender As Object, e As EventArgs) Handles Button_n_9.Click
        setLabelWindowIS("9")
    End Sub

    Private Sub Button_t_3_Click(sender As Object, e As EventArgs) Handles Button_t_3.Click
        InputStringArr.Clear()
        InputString = ""
        Label_window.Text = InputString
        Label_1.Text = InputString
    End Sub

    Private Sub Button_t_4_Click(sender As Object, e As EventArgs) Handles Button_t_4.Click
        If InputString.Length > 0 Then
            InputString = InputString.Remove(InputString.Length - 1)
            Label_window.Text = InputString
        Else
            Label_window.Text = InputString
        End If
    End Sub

    Private Sub Button_t_1_Click(sender As Object, e As EventArgs) Handles Button_t_1.Click
        setLabelWindowIS("(")
    End Sub

    Private Sub Button_t_2_Click(sender As Object, e As EventArgs) Handles Button_t_2.Click
        setLabelWindowIS(")")
    End Sub

    Private Sub Button_t_5_Click(sender As Object, e As EventArgs) Handles Button_t_5.Click
        setLabelWindowIS("+")
    End Sub

    Private Sub Button_t_6_Click(sender As Object, e As EventArgs) Handles Button_t_6.Click
        setLabelWindowIS("-")
    End Sub

    Private Sub Button_t_7_Click(sender As Object, e As EventArgs) Handles Button_t_7.Click
        setLabelWindowIS("×")
    End Sub

    Private Sub Button_t_8_Click(sender As Object, e As EventArgs) Handles Button_t_8.Click
        setLabelWindowIS("÷")
    End Sub
    Private Sub setNumberArrFromStack(numberArr As ArrayList, signStack As Stack)
        Dim i As Integer
        For i = 0 To signStack.Count - 1 Step 1
            numberArr.Add(signStack.Pop)
        Next i
    End Sub


    Private Sub Button_t_result_Click(sender As Object, e As EventArgs) Handles Button_t_result.Click
        '일단 기본적인것만 우선 오류제어
        If InputString = "" Then
            Return
        ElseIf Not IsNumeric(InputString(InputString.Length - 1)) Then
            Select Case InputString(InputString.Length - 1)
                Case "+"
                    Return
                Case "-"
                    Return
                Case "×"
                    Return
                Case "÷"
                    Return
            End Select
        End If

        '입력된 식 분리하기
        setInputStringArr()

        '원본 계산식 저장 생성
        Dim openBracket As Integer = 0

        '후위 표기법 변환
        Dim i As Integer
        For i = 0 To InputStringArr.Count - 1 Step 1
            If IsNumeric(InputStringArr(i)) Then
                ' 숫자면 그냥 넣음
                bkCalulators(openBracket).numberArr.Add(InputStringArr(i))

            Else
                '기호면 몇개 체크해야함
                If InputStringArr(i) = "(" Then
                    '괄호가 열리면 
                    openBracket += 1
                    Dim calculatorInstance As New Calculator With {
                        .numberArr = New ArrayList,
                        .signStack = New Stack
                    }
                    bkCalulators.Add(calculatorInstance)

                ElseIf InputStringArr(i) = ")" Then
                    '괄호가 닫히면 삭제
                    setNumberArrFromStack(bkCalulators(openBracket).numberArr, bkCalulators(openBracket).signStack)
                    '이전 배열로 다 추가
                    Dim u As Integer
                    For u = 0 To bkCalulators(openBracket).numberArr.Count - 1 Step 1
                        bkCalulators(openBracket - 1).numberArr.Add(bkCalulators(openBracket).numberArr(u))
                    Next u
                    bkCalulators.RemoveAt(openBracket)
                    openBracket -= 1

                ElseIf bkCalulators(openBracket).signStack.Count = 0 And (InputStringArr(i) = "+" Or InputStringArr(i) = "-") Then
                    '스택의 처음 +-이런건 그냥 넣음
                    bkCalulators(openBracket).signStack.Push(InputStringArr(i))

                ElseIf InputStringArr(i) = "+" Or InputStringArr(i) = "-" Then
                    '그다음 +-는 스택이랑 교환
                    setNumberArrFromStack(bkCalulators(openBracket).numberArr, bkCalulators(openBracket).signStack)
                    bkCalulators(openBracket).signStack.Clear()
                    bkCalulators(openBracket).signStack.Push(InputStringArr(i))

                ElseIf InputStringArr(i) = "×" Or InputStringArr(i) = "÷" Then
                    '*/는 스택에 넣음
                    bkCalulators(openBracket).signStack.Push(InputStringArr(i))

                End If
            End If
        Next i

        '마지막 스택에 있는거 다넣음
        setNumberArrFromStack(bkCalulators(openBracket).numberArr, bkCalulators(openBracket).signStack)

        '후위연산 출력
        Dim tmp As String = ""
        For i = 0 To bkCalulators(openBracket).numberArr.Count - 1 Step 1
            tmp = tmp & bkCalulators(openBracket).numberArr(i)
        Next
        Label_1.Text = tmp

        '실제 계산
        '늦은바인딩에러떠서 배열을 새로만들어야함
        Dim result As New ArrayList
        result = setResultNumberArr(bkCalulators(openBracket).numberArr)
        Label_window.Text = CStr(result(0))

        bkCalulators(openBracket).numberArr.Clear()
        bkCalulators(openBracket).signStack.Clear()
        result.Clear()
        InputStringArr.Clear()
        InputString = ""

    End Sub

    Private Function setResultNumberArr(numberArr As ArrayList) As ArrayList
        '계산해서 배열 반환
        Dim tmp As Integer = 0
        Dim i As Integer = 0

        While Not numberArr.Count = 1
            If Not IsNumeric(numberArr(i)) Then

                Select Case numberArr(i)
                    Case "+"
                        tmp = CInt(numberArr(i - 2)) + CInt(numberArr(i - 1))
                    Case "-"
                        tmp = CInt(numberArr(i - 2)) - CInt(numberArr(i - 1))
                    Case "×"
                        tmp = CInt(numberArr(i - 2)) * CInt(numberArr(i - 1))
                    Case "÷"
                        tmp = CInt(numberArr(i - 2)) \ CInt(numberArr(i - 1))

                End Select

                numberArr(i - 2) = tmp
                numberArr.RemoveAt(i)
                numberArr.RemoveAt(i - 1)

                numberArr = setResultNumberArr(numberArr)
            End If

            i += 1
        End While
        Return numberArr
    End Function

    Private Sub setInputStringArr()
        '분리하기
        Dim tmp As String = ""
        Dim i As Integer
        For i = 0 To InputString.Length - 1 Step 1
            If Not IsNumeric(InputString(i)) Then
                InputStringArr.Add(tmp)
                tmp = ""
                InputStringArr.Add(InputString(i))
            Else
                tmp = tmp & InputString(i)
            End If
        Next i
        InputStringArr.Add(tmp)
    End Sub
End Class

 

 

728x90
반응형