簡単な文字列の計算式を計算する

四則演算だけ、括弧にすら対応してない
格好つけてデリゲートとか使ったら逆に分かりづらくなった感が…

四則演算だけ、括弧にすら対応してない
格好つけてデリゲートとか使ったら逆に分かりづらくなった感が…

  • 9k6z
  • 2011/7/27 9:45
  • タグ:
  • タグはありません
Imports System.Text.RegularExpressions

Module Module1

    Delegate Function CalcMethod(ByVal exp1 As Decimal, ByVal exp2 As Decimal) As Decimal

    Sub Main()
        Console.WriteLine(CalculateFromString("-0.2-0.4"))
        '-0.6
        Console.WriteLine(CalculateFromString("50.5/20.5-15.2*4.5+100*2"))
        '134.06341463414634146341463415
        Console.WriteLine(CalculateFromString("50-(5+20)"))
        '0(ERROR)
        Console.ReadKey()
    End Sub

    Public Function CalculateFromString(ByVal expression As String) As Decimal
        '割り算と掛け算を計算する
        CalcDivisionAndMultiplication(expression)
        '足し算と引き算を計算する
        CalcPlusAndMinus(expression)
        'Decimalに変換して返す
        Dim retDec As Decimal = 0
        Decimal.TryParse(expression, retDec)
        Return retDec
    End Function

    Private Sub CalcDivisionAndMultiplication(ByRef expression As String)
        Const DivisionSign As Char = "/"c
        Const MultiplicationSign As Char = "*"c
        While True
            Dim division As Integer = expression.IndexOf(DivisionSign)
            Dim multiplication As Integer = expression.IndexOf(MultiplicationSign)
            If division > 0 AndAlso (division < multiplication OrElse 0 > multiplication) Then
                If Not CalcBySign(expression, DivisionSign, AddressOf CalcMethodDivision) Then Exit While
            ElseIf multiplication > 0 AndAlso (multiplication < division OrElse 0 > division) Then
                If Not CalcBySign(expression, MultiplicationSign, AddressOf CalcMethodMultiplication) Then Exit While
            Else
                Exit While
            End If
        End While
    End Sub

    Private Sub CalcPlusAndMinus(ByRef expression As String)
        Const PlusSign As Char = "+"c
        Const MinusSign As Char = "-"c
        While True
            Dim plus As Integer = expression.IndexOf(PlusSign)
            Dim minus As Integer = expression.IndexOf(MinusSign)
            If minus = 0 Then minus = expression.IndexOf(MinusSign, minus + 1)
            If plus > 0 AndAlso (plus < minus OrElse 0 >= minus) Then
                If Not CalcBySign(expression, PlusSign, AddressOf CalcMethodPlus) Then Exit While
            ElseIf minus > 0 AndAlso (minus < plus OrElse 0 > plus) Then
                If Not CalcBySign(expression, MinusSign, AddressOf CalcMethodMinus) Then Exit While
            Else
                Exit While
            End If
        End While
    End Sub

    Private Function CalcBySign(ByRef exp As String, ByVal sign As Char, ByVal method As CalcMethod) As Boolean
        Dim ptn As String = "(?<exp1>-?(\d|\.)+)" & Regex.Escape(sign) & "(?<exp2>-?(\d|\.)+)"
        Dim match As Match = Regex.Match(exp, ptn)
        If match.Success Then
            Dim exp1 As Decimal = CDec(match.Groups("exp1").Value)
            Dim exp2 As Decimal = CDec(match.Groups("exp2").Value)
            exp = exp.Remove(match.Index, match.Length)
            exp = exp.Insert(match.Index, CStr(method(exp1, exp2)))
        End If
        Return match.Success
    End Function

    Private Function CalcMethodDivision(ByVal exp1 As Decimal, ByVal exp2 As Decimal) As Decimal
        If exp2 = 0 Then Return 0 '例外をスローした方がいいかな?
        Return exp1 / exp2
    End Function

    Private Function CalcMethodMultiplication(ByVal exp1 As Decimal, ByVal exp2 As Decimal) As Decimal
        Return exp1 * exp2
    End Function

    Private Function CalcMethodPlus(ByVal exp1 As Decimal, ByVal exp2 As Decimal) As Decimal
        Return exp1 + exp2
    End Function

    Private Function CalcMethodMinus(ByVal exp1 As Decimal, ByVal exp2 As Decimal) As Decimal
        Return exp1 - exp2
    End Function

End Module