欢迎访问infoheader的Blog——年华似水浪淘沙
E-mail:infoheader@gmail.com
GTalk:infoheader@gmail.com
来自fanfou:

2008年12月27日星期六

VB表达式计算函数

今天自己写的一个函数,没想到会用了将近两个小时……
还没有进行优化,估计这个函数的效率不怎么样。
没有错误检查功能。如果表达式有问题,那么结果是不可预知的。
写完后调试了半天,检查出多处错误。
如果发现还存在问题请告诉我。
感谢声明:感谢Aegisys[0GiNr]发现未检查减法运算的错误

'函数:Calc(Exp As String) As Double
'用于计算包含括号、四则运算的字符串表达式

'infoheader版权所有,转载、使用请注明出处
'Blog:infoheader.blogspot.com
'E-mail:infoheader@gamil.com
'仅供技术交流使用,严禁用于商业用途


Public Function Calc(Exp As String) As Double
'去空格
Exp = Replace(Exp, " ", "")
'去掉最外括号
While Left(Exp, 1) = "(" And Right(Exp, 1) = ")"
Exp = Mid(Exp, 2, Len(Exp) - 2)
Wend
Calc = CalcA(Exp)
End Function

Public Function CalcA(Exp As String) As Double
'Deep = Deep + 1
'(1+2)*3+4*5+2=?
'(1+2)*(3+4)+5=?
'(1+2)*((3+4)*5)+3=?
'11111012222211100

DoEvents '防止死机
Dim LvS As Integer

'循环变量
Dim I As Integer, J As Integer

'对于纯数值
If Trim(Str(Val(Exp))) = Trim(Exp) Then
CalcA = Val(Exp)
GoTo LAB_END
ElseIf Exp = "" Then '对于空(负号)
CalcA = 0
GoTo LAB_END
End If

'Dim AscN As Integer
'For I = 1 To Len(Exp)
' AscN = Asc(Mid(Exp, I, 1))
' If AscN > 57 Or AscN < 43 Or AscN = 47 Or AscN = 44 Then GoTo LAB_NUM
'Next
'CalcA = Val(Exp)
'GoTo LAB_END
'LAB_NUM:

'括号跳转
For I = 1 To Len(Exp)
If Mid(Exp, I, 1) = "(" Then
LvS = I
GoTo LAB_A1
End If
Next
GoTo LAB_A2

LAB_A1:
'对于有括号的表达式
'存储级别(优化备用)
'Dim Lv() As Integer
'ReDim Lv(Len(Exp)) As Integer
'当前级别,最大级别,最大级别启始,结束,判定
Dim CurrentLv As Integer, LvM As Integer, LvMS As Integer, LvME As Integer, LvMB As Boolean

CurrentLv = 0: LvMB = False
'定级
For I = LvS To Len(Exp)
If Mid(Exp, I, 1) = "(" Then
CurrentLv = CurrentLv + 1
If CurrentLv > LvM Then
LvM = CurrentLv
LvMS = I '最大级别启始点
LvMB = True
End If
ElseIf Mid(Exp, I, 1) = ")" Then
CurrentLv = CurrentLv - 1
If LvMB Then
LvME = I '最大级别结束点
LvMB = False
End If
End If
'Lv(I) = CurrentLv '存储级别(优化备用)
Next
'计算最高级括号
Exp = Left(Exp, LvMS - 1) & CalcA(Mid(Exp, LvMS + 1, LvME - LvMS - 1)) & Right(Exp, Len(Exp) - LvME)
CalcA = CalcA(Exp)
GoTo LAB_END

LAB_A2:
'对于没有括号的表达式
'加减法跳转
Dim AscS As Integer
For I = 1 To Len(Exp)
If Mid(Exp, I, 1) = "+" Then GoTo LAB_B1
If Mid(Exp, I, 1) = "-" And I > 1 Then
AscS = Asc(Mid(Exp, I - 1, 1))
If AscS >= Asc("0") And AscS < Asc("9") Then GoTo LAB_B1
End If
Next
GoTo LAB_B2
LAB_B1:
'对于有加减法的表达式
For I = 1 To Len(Exp)
'计算加减法
If Mid(Exp, I, 1) = "+" Then
Exp = CalcA(Left(Exp, I - 1)) + CalcA(Right(Exp, Len(Exp) - I))
CalcA = CalcA(Exp)
GoTo LAB_END
ElseIf Mid(Exp, I, 1) = "-" Then
Exp = Str(CalcA(Left(Exp, I - 1)) - CalcA(Right(Exp, Len(Exp) - I)))
CalcA = CalcA(Exp)
GoTo LAB_END
End If
Next
LAB_B2:
'对于没有加减法的表达式
For I = 1 To Len(Exp)
'计算乘除法
If Mid(Exp, I, 1) = "*" Then
Exp = Str(CalcA(Left(Exp, I - 1)) * CalcA(Right(Exp, Len(Exp) - I)))
CalcA = CalcA(Exp)
GoTo LAB_END
ElseIf Mid(Exp, I, 1) = "/" Then
Exp = Str(CalcA(Left(Exp, I - 1)) / CalcA(Right(Exp, Len(Exp) - I)))
CalcA = CalcA(Exp)
GoTo LAB_END
End If
Next
LAB_END:
'Deep = Deep - 1
End Function

6 条评论:

匿名 说...

好多的GOTO。。。。

infoheader 说...

1#
因为Goto在这里有优化算法的作用。
Goto依然存在于VB而没有被抛弃,是因为在有的地方它依然起着无可替代的作用。

Kingsam Chen 说...

嗯,在出错设计的时候,Goto有着天生的优势~

匿名 说...

好像不支持 0.5 这种小数, 只能写作 .5

infoheader 说...

@Kingsam Chen
On Error GoTo Err1
...
Exit Sub
Err1:
...
(Resume Next)

infoheader 说...

@iceboy
啊……貌似是有这个问题。
因为“0.5”被认为不是一个数字……
一会儿修正一下。