学习Excel技术,关注微信公众号:
excelperfect
在前面的文章中,我们用VBA实现了栈数据结构,也实现了算术表达式的拆分,并实现了将拆分后的算术表达式由中缀表达式转换成计算机容易计算的后缀表达式,具体内容详见下列4篇文章:
1.基础扩展 | 13. 使用VBA实现栈结构
2.基础扩展 | 14. 栈结构应用基础示例
3.VBA编程练习01. 拆分算术表达式
4.VBA编程练习02. 中缀表达式转换成后缀表达式
练习:根据上述基础知识,编写实现四则混合运算的VBA代码。
运用前面的知识,我们可以将示例表达式:
9 + (3 – 1) * 3 + 10 /2
转换成后缀表达式:
9 3 1 – 3 * + 10 2 / +
然后,应用下面的规则来计算表达式值。
规则:从左到右遍历算术表达式的每个数字和符号,遇到是数字就入栈,遇到是符号,就将处于栈顶的两个数字出栈,并进行运算,将运算结果入栈,一直到最终获得结果。
关键点:使用栈结构来存放数字,并在遇到符号时将栈顶的两个数字出栈并使用符号进行运算,再将得到的结果入栈。详细的原理请参见程杰著的《大话数据结构》P106-107中的内容
VBA代码:
'创建栈
Dim myStackTwo As NewStack
'计算表达式值
Sub MyCalculator()
Dim strMyExpress As String
Dim CalExpress() As String
Dim NumOne, NumTwo, NumTemp
Dim i As Long
strMyExpress = "9+(3-1)*3+10/2"
CalExpress = ConvertExpress(strMyExpress)
For i = LBound(CalExpress) To UBound(CalExpress)
'如果是数字,则入栈
If IsNumeric(CalExpress(i)) Then
myStackTwo.Push CalExpress(i)
Else
'否则位于栈顶的两个数字出栈
NumOne = myStackTwo.Pop
NumTwo = myStackTwo.Pop
'根据符号进行运算
Select Case CalExpress(i)
Case "+"
NumTemp = NumTwo + NumOne
Case "-"
NumTemp = NumTwo - NumOne
Case "*"
NumTemp = NumTwo * NumOne
Case "/"
NumTemp = NumTwo / NumOne
End Select
'将计算结果入栈
myStackTwo.Push NumTemp
End If
Next i
'此时在栈顶的元素就是计算结果
Debug.Print "计算结果:" & myStackTwo.Pop
End Sub
代码中,用到了《VBA编程练习02.中缀表达式转换成后缀表达式》中的程序,我将其改写成了一个返回数组的函数:
'创建栈
Dim MyStack As NewStack
'转换成后缀表达式
Function ConvertExpress(strExpress As String) As String()
Dim SuffixExpress As String
'声明放置表达式各元素的数组
Dim MidExpress() As String
Dim BackExpress() As String
'声明其它变量
Dim i As Long, j As Long
Dim iCount As Long
Dim str
MidExpress = SplitExpress(strExpress)
'遍历表达式
For i = LBound(MidExpress) To UBound(MidExpress)
'如果是数字则直接放入输出数组
If IsNumeric(MidExpress(i)) Then
iCount = iCount + 1
ReDim Preserve BackExpress(1 ToiCount)
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
Do While 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 Not MyStack.StackEmpty
iCount = iCount + 1
ReDim Preserve BackExpress(1 To iCount)
BackExpress(iCount) = MyStack.Pop
Loop
ConvertExpress = BackExpress()
End Function
上面的代码又用到了《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 > 0 Then
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 ToiCount)
var2(iCount) = var1(i)
End Select
Next i
'返回结果
SplitExpress = var2()
End Function
运行MyCalculator过程的结果为:
下面是代码的图片版:
|
|