Evaluate a string expression/Array for Operands

T

Travis

strExpression = "1+2+3"

Say I have an expression written in a string. Is there some way that can be
evaluated? Order of precedence does not matter, it should just go from left
to right.

I had an idea using arrays. I figured I could make two arrays, one for the
numbers, one for the operands. It would look at the first two numbers in the
NumberArray, and do the math based on the first operand in the OperandArray.
Then it would take that total, and use it against the third number doing the
math of the next operand, etc. It would work, but I have a problem with this
too. Of what type are operands? My NumberArray is a decimal array. I have no
idea what to make my OperandArray.

So maybe that idea isn't possible. If anyone could help me with either
method that would be great! Either converting a string expression into a
mathematical expression that can be calculated, or how to make an array to
hold *usable* operands.

Thanks!
 
M

Michel Posseth [MCP]

Travis said:
strExpression = "1+2+3"

Say I have an expression written in a string. Is there some way that can
be evaluated? Order of precedence does not matter, it should just go from
left to right.

I had an idea using arrays. I figured I could make two arrays, one for the
numbers, one for the operands. It would look at the first two numbers in
the NumberArray, and do the math based on the first operand in the
OperandArray. Then it would take that total, and use it against the third
number doing the math of the next operand, etc. It would work, but I have
a problem with this too. Of what type are operands? My NumberArray is a
decimal array. I have no idea what to make my OperandArray.

So maybe that idea isn't possible. If anyone could help me with either
method that would be great! Either converting a string expression into a
mathematical expression that can be calculated, or how to make an array to
hold *usable* operands.

Thanks!


what you want is an eval function ,, there are lots of ways to acomplish
this
here are a few solutions i like personly the most

http://www.codeproject.com/KB/vb/expression_evaluator.aspx

and the best i have ever seen that is multi purpose and simple

http://www.eggheadcafe.com/articles/20030908.asp


HTH

Michel Posseth
 
T

Tom Shelton

strExpression = "1+2+3"

Say I have an expression written in a string. Is there some way that can be
evaluated? Order of precedence does not matter, it should just go from left
to right.

I had an idea using arrays. I figured I could make two arrays, one for the
numbers, one for the operands. It would look at the first two numbers in the
NumberArray, and do the math based on the first operand in the OperandArray.
Then it would take that total, and use it against the third number doing the
math of the next operand, etc. It would work, but I have a problem with this
too. Of what type are operands? My NumberArray is a decimal array. I have no
idea what to make my OperandArray.

So maybe that idea isn't possible. If anyone could help me with either
method that would be great! Either converting a string expression into a
mathematical expression that can be calculated, or how to make an array to
hold *usable* operands.

Thanks!

Here is a method that works well, and suprisingly fast....
http://tomshelton.wordpress.com/200...aluating-mathematical-expressions-at-runtime/

HTH
 
C

Chris Dunaway

strExpression = "1+2+3"

Say I have an expression written in a string. Is there some way that can be
evaluated? Order of precedence does not matter, it should just go from left
to right.

I had an idea using arrays. I figured I could make two arrays, one for the
numbers, one for the operands. It would look at the first two numbers in the
NumberArray, and do the math based on the first operand in the OperandArray.
Then it would take that total, and use it against the third number doing the
math of the next operand, etc. It would work, but I have a problem with this
too. Of what type are operands? My NumberArray is a decimal array. I have no
idea what to make my OperandArray.

So maybe that idea isn't possible. If anyone could help me with either
method that would be great! Either converting a string expression into a
mathematical expression that can be calculated, or how to make an array to
hold *usable* operands.

Thanks!

I wrote this simple class for evaluating expressions. I'm sure it
could use some improvements but perhaps it can help you::

Public Class SimpleEval

Private Shared _operators() As String = {"-", "+", "/", "*", "^"}
Private Shared _operations() As Func(Of Double, Double, Double) =
{Function(a1, a2) a1 - a2, Function(a1, a2) a1 + a2, Function(a1, a2)
a1 / a2, Function(a1, a2) a1 * a2, Function(a1, a2) a1 ^ a2}

Public Shared Function Eval(ByVal expression As String) As Double

Dim tokens As List(Of String) = getTokens(expression)
Dim operandStack As New Stack(Of Double)
Dim operatorStack As New Stack(Of String)
Dim tokenIndex As Integer = 0

While tokenIndex < tokens.Count
Dim token As String = tokens(tokenIndex)
If token = "(" Then
Dim subExpr As String = getSubExpression(tokens,
tokenIndex)
operandStack.Push(Eval(subExpr))
Continue While
End If
If token = ")" Then
Throw New ArgumentException("Mis-matched parentheses
in expression")
End If
If Array.IndexOf(_operators, token) >= 0 Then 'If this
is an operator
While operatorStack.Count > 0 AndAlso Array.IndexOf
(_operators, token) < Array.IndexOf(_operators, operatorStack.Peek())
Dim op As String = operatorStack.Pop()
Dim arg2 As Double = operandStack.Pop()
Dim arg1 As Double = operandStack.Pop()
operandStack.Push(_operations(Array.IndexOf
(_operators, op))(arg1, arg2))
End While
operatorStack.Push(token)
Else
operandStack.Push(Double.Parse(token))
End If

tokenIndex += 1
End While

While operatorStack.Count > 0
Dim op As String = operatorStack.Pop()
Dim arg2 As Double = operandStack.Pop()
Dim arg1 As Double = operandStack.Pop()
operandStack.Push(_operations(Array.IndexOf(_operators,
op))(arg1, arg2))
End While

Return operandStack.Pop()

End Function

Private Shared Function getSubExpression(ByVal tokens As List(Of
String), ByRef index As Integer) As String
Dim subExpr As New StringBuilder()

Dim parenlevels As Integer = 1

index += 1
While index < tokens.Count AndAlso parenlevels > 0
Dim token As String = tokens(index)
If tokens(index) = "(" Then
parenlevels += 1
End If

If tokens(index) = ")" Then
parenlevels -= 1
End If

If parenlevels > 0 Then
subExpr.Append(token)
End If

index += 1
End While

If (parenlevels > 0) Then
Throw New ArgumentException("Mis-matched parentheses in
expression")
End If

Return subExpr.ToString()
End Function

Private Shared Function getTokens(ByVal expression As String) As
List(Of String)
Dim operators As String = "()^*/+-"
Dim tokens As New List(Of String)()
Dim sb As New StringBuilder()

For Each c As Char In expression.Replace(" ", String.Empty)
If operators.IndexOf(c) >= 0 Then
If (sb.Length > 0) Then
tokens.Add(sb.ToString())
sb.Length = 0
End If
tokens.Add(c)
Else
sb.Append(c)
End If
Next

If (sb.Length > 0) Then
tokens.Add(sb.ToString())
End If

Return tokens
End Function
End Class


Chris
 
J

James Pemberton

It just so happens I had a need for just the function you are asking about.
The code I am pasting actually breaks down formulas with embedded field
names or simple calculation. It might be a bit deeper than you wanted and
it might not be the best code in the world, but it worked for my needs.

I hope this Helps

Sample calculations:
"CALC{(<3:mLAdded>-<2:BlankValue>*<1:Factor>*100)/(<3:mLAdded>-<2:BlankValue>*<1:Factor>+<4:BoneDry>)}"
"CALC{<1:TestSample1>+<2:TestSample2>+<3:TestSample3>/3}"
"CALC{1+1}

Code:
Public Class CalculationBreakdown
#Region "Variables"
Private arFields() As String
Private arValues() As Double
Private FieldCount, stridx As Integer
Private fieldname As String
Private s_calculatedstring As String
#End Region

#Region "Properties"
Public ReadOnly Property Fields() As String()
Get
Return arFields
End Get
End Property
Public Property Values() As Double()
Get
Return arValues
End Get
Set(ByVal value As Double())
arValues = value
End Set
End Property
#End Region

#Region "Constructor"
Public Sub New()

End Sub
#End Region

#Region "Methods"
Public Sub ClearArrays()
arFields = Nothing
arValues = Nothing
FieldCount = 0
s_calculatedstring = Nothing
End Sub
Public Sub FieldBreakdown(ByVal CalculationString As String)
s_calculatedstring = CalculationString
Do
FieldCount += 1
stridx = s_calculatedstring.IndexOf(FieldCount.ToString + ":")
If stridx = -1 Then
Exit Do
End If
stridx += 2
fieldname = GetFieldName(0,
s_calculatedstring.Substring(stridx))
ReDim Preserve arFields(FieldCount - 1)
ReDim Preserve arValues(FieldCount - 1)
arFields(FieldCount - 1) = fieldname
arValues(FieldCount - 1) = 0
Loop
End Sub

Public Sub FieldSetValue(ByVal Fieldname As String, ByVal FieldValue As
Double)
Dim aridx As Integer = Array.IndexOf(arFields, Fieldname)
If aridx > -1 Then
arValues(aridx) = FieldValue
End If
End Sub

Private Function GetFieldName(ByVal stridx As Integer, ByVal fieldstring
As String) As String
Dim fieldname As String = Nothing
Dim endidx As Integer = 0
endidx = fieldstring.IndexOf(">", stridx)
fieldname = fieldstring.Substring(stridx, endidx - stridx)
Return fieldname
End Function
#End Region

#Region "Functions"
Public Function CalculatedValue(ByVal CalculationString As String) As
Double
Dim returnvalue As Double = 0
If s_calculatedstring.IndexOf("(") > -1 Then
returnvalue = CalculateMultiples(CalculationString)
Else
returnvalue = Calculate(CalculationString)
End If
Return returnvalue
End Function


Private Function CalculateMultiples(ByVal calcstring As String) As
Double
Dim charidx, valuescnt, endidx As Integer
Dim newstring As String = Nothing
Dim charchk As Char
Dim calcvalue As Double = 0

charidx = 0
valuescnt = -1
Do Until charidx > calcstring.Length - 1
charchk = calcstring.Substring(charidx)
Select Case charchk
Case "("
endidx = calcstring.IndexOf(")", charidx)
newstring += Calculate(calcstring.Substring(charidx + 1,
endidx - charidx - 1)).ToString
charidx = endidx
Case "+", "-", "*", "/"
newstring += charchk
Case Else
If Char.IsNumber(charchk) OrElse
charchk.ToString.Equals(".") Then
newstring += charchk
End If
End Select
charidx += 1
Loop
If newstring IsNot Nothing Then
calcvalue = Calculate(newstring)
End If
Return calcvalue
End Function

Private Function Calculate(ByVal calcstring As String) As Double
Dim fieldstr As String = Nothing
Dim charchk As Char
Dim charidx, valuescnt As Integer
Dim calcvalue As Double = 0
Dim addvalue As Boolean = False
Dim values() As Double = Nothing

charidx = 0
valuescnt = -1
Do Until charidx > calcstring.Length - 1
charchk = calcstring.Substring(charidx, 1)

Select Case charchk
Case "<"
charidx = calcstring.IndexOf(":", charidx) + 1
calcvalue = FieldGetValue(GetFieldName(0,
calcstring.Substring(charidx)))
'calcvalue = GetFieldName(0,
calcstring.Substring(charidx))
charidx = calcstring.IndexOf(">", charidx)
addvalue = True
fieldstr = Nothing
Case "+", "-", "*", "/"
fieldstr = Nothing
Case Else
If Char.IsNumber(charchk) OrElse
charchk.ToString.Equals(".") Then
If fieldstr Is Nothing Then
addvalue = True
End If
fieldstr += charchk
calcvalue = CDbl(fieldstr)
End If
End Select
If addvalue Then
valuescnt += 1
ReDim Preserve values(valuescnt)
values(valuescnt) = calcvalue
addvalue = False
Else
If fieldstr IsNot Nothing Then
values(valuescnt) = calcvalue
End If
End If
charidx += 1
Loop

If values IsNot Nothing Then
charidx = 0
valuescnt = 0
calcvalue = values(valuescnt)
Do Until charidx > calcstring.Length - 1
charchk = calcstring.Substring(charidx)
Select Case charchk
Case "+"
valuescnt += 1
calcvalue += values(valuescnt)
Case "-"
valuescnt += 1
calcvalue -= values(valuescnt)
Case "*"
valuescnt += 1
calcvalue *= values(valuescnt)
Case "/"
valuescnt += 1
calcvalue /= values(valuescnt)
End Select
charidx += 1
Loop
End If

Return calcvalue
End Function

Private Function FieldGetValue(ByVal FieldName As String) As Double
Dim returnvalue As Double = 0
Dim aridx As Integer = Array.IndexOf(arFields, FieldName)
If aridx > -1 Then
returnvalue = arValues(aridx)
End If
Return returnvalue
End Function
#End Region

End Class
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top