VBA编程练习02. 中缀表达式转换成后缀表达式

论坛 期权论坛 期权     
完美Excel   2019-7-7 23:58   4214   0
学习Excel技术,关注微信公众号:
excelperfect

在前面的文章中,我们用VBA实现了栈数据结构,也实现了算术表达式的拆分,详见下列3篇文章:
1. 基础扩展 | 13. 使用VBA实现栈结构
2. 基础扩展 | 14. 栈结构应用基础示例
3. VBA编程练习01. 拆分算术表达式

下面,我们运用上述知识,使用VBA代码实现中缀表达式变为后缀表达式的转换。

我们平时使用的标准四则运算表达式称为“中缀表达式”,例如:
9 + (3 – 1) * 3 + 10 / 2
所有的运算符号都在两个数字的中间。转换成后缀表达式应该为:
9 3 1 – 3 * + 10 2 / +

转换规则为:从左至右遍历中缀表达式中的每个数字和符号,若是数字则直接输出;若是符号,则判断其与栈顶符号的优先级,是右括号或者优先级低于栈顶符号(乘除优先于加减),则栈顶元素依次出栈并输出,然后将当前符号入栈;直至最后全部输出为止。

为什么要用到后缀表达式呢?主要是为了方便计算机对算术表达式求值。这种表达方法是由一位波兰逻辑学家发现的,也称为逆波兰(RPN)表示法。

练习:根据上述基础知识和内容,编写实现中缀表达式转换成后缀表达式的VBA代码。

关键点:使用栈结构实现转换规则。详细的原理请参见程杰著的《大话数据结构》P108-110中的内容。

VBA代码:
'创建栈
Dim MyStack As New Stack

Sub ConvertExpress()
    '声明表达式字符串
    Dim strExpress As String
    Dim SuffixExpress AsString
    '声明放置表达式各元素的数组
    Dim MidExpress() As String
    Dim BackExpress() AsString
    '声明其它变量
    Dim i As Long, j As Long
    Dim iCount As Long
    Dim str
      
    '测试表达式
    strExpress ="9+(3-1)*3+10/2"
'    strExpress ="9*2/3+2"
'    strExpress ="9*3+(18-9)/3+2"
    MidExpress =SplitExpress(strExpress)
   
    '遍历表达式
    For i = LBound(MidExpress) To UBound(MidExpress)
       '如果是数字则直接放入输出数组
        If IsNumeric(MidExpress(i)) Then
            iCount = iCount +1
            ReDim Preserve BackExpress(1 To iCount)
           BackExpress(iCount) = MidExpress(i)
       '如果是符号则根据括号配对或者四则运算规则
       '将运算符号放入输出数组
        Else
            Select Case MidExpress(i)
                Case"{", "[", "("
                   MyStack.Push MidExpress(i)
                Case"}", "]", ")"
               '遇到右括号,则栈顶元素出栈
               '直到遇到配对的左括号
                    str =MyStack.StackTop
                    Do While((str  "{") And (str  "[") And (str "("))
                        iCount= iCount + 1
                        ReDim Preserve BackExpress(1 To iCount)
                       BackExpress(iCount) = MyStack.Pop
                        str =MyStack.StackTop
                    Loop
                   MyStack.Pop
               '乘除优先级最大直接入栈
                Case"*", "/"
                    str =MyStack.StackTop
                   MyStack.Push MidExpress(i)
               '加减时遇到栈顶为乘除
               '则全部出栈并将其放入输出数组
                Case"+", "-"
                    str =MyStack.StackTop
                    If str = "*" Orstr = "/" Then
                        DoWhile Not MyStack.StackEmpty
                           iCount = iCount + 1
                           ReDim Preserve BackExpress(1 To iCount)
                           BackExpress(iCount) = MyStack.Pop
                        Loop
                    End If
                   MyStack.Push MidExpress(i)
            End Select
        End If
    Next i
   
    '将栈中剩余的元素全部出栈并放入输出数组
    Do While NotMyStack.StackEmpty
        iCount = iCount + 1
        ReDim Preserve BackExpress(1 To iCount)
        BackExpress(iCount) =MyStack.Pop
    Loop
   
    '联接各元素成输出的后缀表达式
    str = ""
    For i =LBound(BackExpress) To UBound(BackExpress)
         str = str & BackExpress(i) & " "
    Next i
   
    Debug.Print str
End Sub

代码中,用到了《VBA编程练习01.拆分算术表达式》中的程序,我将其改写成了一个返回数组的函数:
Function SplitExpress(express As String) As String()
    '存储表达式的每个字符
    Dim var1() As String
    '存储表达式中各元素(符号和数字)
    Dim var2() As String
    '循环变量
    Dim i As Long
    Dim j As Long
    '计数,用来确定动态数组大小
    Dim iCount As Long
    '表达式长度
    Dim lLen As Long
    '临时变量,用来存储数字元素中单个数字数
    Dim temp As Long
    '将相邻的数字组合成一个数字元素
    Dim str  As String
   
    lLen = Len(express)
    '重定义数组大小为表达式长度
    '比表达式的长度大1
    '确保最后一个字符能添加到数组
    ReDim var1(1 To lLen + 1)
   
    '将表达式拆分单个字符
    For i = 1 To lLen
        var1(i) = Mid(express,i, 1)
    Next i
   
    temp = 0
   
    '遍历表达式
    For i = 1 To lLen + 1
       '如果相邻字符是数字,则将其取出并连接成一个数字
        If var1(i) Like"[0-9]" Then
            temp = temp + 1
        ElseIf temp > 0Then
            For j = 1 To temp
                str = str& var1(i - temp + j - 1)
            Next j
            iCount = iCount +1
            ReDim Preserve var2(1 To iCount)
            var2(iCount) = str
            temp = 0
            str = ""
        End If
      
       '如果是符号,则直接存放
        Select Case var1(i)
            Case"{", "[", "(", ")", "}","]", "+", "-", "*", "/"
                iCount =iCount + 1
                ReDim Preserve var2(1 To iCount)
                var2(iCount) =var1(i)
        End Select
    Next i
   
    '返回结果
    SplitExpress = var2()
End Function

运行ConvertExpress过程的结果为:



下面是代码的图片版:



分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:195
帖子:39
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP