Replace a^b by Pow(a,b)

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
3
down vote

favorite












The application uses NCalc to execute diverse calculations.



The 'natural' way to write powers using a computer is using the ^ symbol. However, NCalc already uses it as bitwise or.



I use the following function to parse calculations and convert it to the Pow(a,b) format.



 Private Function _replacePower(Str As String) As String
Const PowerString As String = "Pow(0,1)"

Dim i, j As Integer
Dim c As String
Dim before, after, all As String

Dim other_p As Integer 'Keep track of other opening/closing parenthesis, to avoid breaking for a nested one.

other_p = -1
i = 1
While i <= Len(Str)
c = Mid(Str, i, 1)
If c = "^" Then
If Mid(Str, i - 1, 1) = ")" Then
j = i - 1
Do While Mid(Str, j, 1) <> "(" Or other_p > 0
If Mid(Str, j, 1) = ")" Then other_p = other_p + 1
If Mid(Str, j, 1) = "(" Then other_p = other_p - 1
j = j - 1
Loop
before = Mid(Str, j, i - j)
Else
j = i - 1
'The expression to be raised is everything between the power and + - * / , ( <- Opening parenthesis is not ok if there is no closing one, and this case is handled above.
Do While (Mid(Str, j, 1) <> "+" And Mid(Str, j, 1) <> "-" And Mid(Str, j, 1) <> "*" And Mid(Str, j, 1) <> "/" And Mid(Str, j, 1) <> "," And Mid(Str, j, 1) <> "(")
j = j - 1
If j = 0 Then Exit Do
Loop
before = Mid(Str, j + 1, i - j - 1)
End If

other_p = -1

If Mid(Str, i + 1, 1) = "(" Or other_p > 0 Then
j = i + 1
Do While Mid(Str, j, 1) <> ")"
If Mid(Str, j, 1) = ")" Then other_p = other_p - 1
If Mid(Str, j, 1) = "(" Then other_p = other_p + 1
j = j + 1
Loop
after = Mid(Str, i + 1, j - i)
Else
j = i + 1
Do While (Mid(Str, j, 1) <> "+" And Mid(Str, j, 1) <> "-" And Mid(Str, j, 1) <> "*" And Mid(Str, j, 1) <> "/" And Mid(Str, j, 1) <> "," And Mid(Str, j, 1) <> ")")
j = j + 1
If j = Len(Str) + 1 Then Exit Do
Loop
after = Mid(Str, i + 1, j - i - 1)
End If

all = before & "^" & after
Str = Replace(Str, all, String.Format(PowerString, before, after))
i = 1
End If
i = i + 1
End While
Return Str
End Function


My issues lies with performance, as it appears that this function is the bottleneck of the entire application. How could I improve?







share|improve this question



























    up vote
    3
    down vote

    favorite












    The application uses NCalc to execute diverse calculations.



    The 'natural' way to write powers using a computer is using the ^ symbol. However, NCalc already uses it as bitwise or.



    I use the following function to parse calculations and convert it to the Pow(a,b) format.



     Private Function _replacePower(Str As String) As String
    Const PowerString As String = "Pow(0,1)"

    Dim i, j As Integer
    Dim c As String
    Dim before, after, all As String

    Dim other_p As Integer 'Keep track of other opening/closing parenthesis, to avoid breaking for a nested one.

    other_p = -1
    i = 1
    While i <= Len(Str)
    c = Mid(Str, i, 1)
    If c = "^" Then
    If Mid(Str, i - 1, 1) = ")" Then
    j = i - 1
    Do While Mid(Str, j, 1) <> "(" Or other_p > 0
    If Mid(Str, j, 1) = ")" Then other_p = other_p + 1
    If Mid(Str, j, 1) = "(" Then other_p = other_p - 1
    j = j - 1
    Loop
    before = Mid(Str, j, i - j)
    Else
    j = i - 1
    'The expression to be raised is everything between the power and + - * / , ( <- Opening parenthesis is not ok if there is no closing one, and this case is handled above.
    Do While (Mid(Str, j, 1) <> "+" And Mid(Str, j, 1) <> "-" And Mid(Str, j, 1) <> "*" And Mid(Str, j, 1) <> "/" And Mid(Str, j, 1) <> "," And Mid(Str, j, 1) <> "(")
    j = j - 1
    If j = 0 Then Exit Do
    Loop
    before = Mid(Str, j + 1, i - j - 1)
    End If

    other_p = -1

    If Mid(Str, i + 1, 1) = "(" Or other_p > 0 Then
    j = i + 1
    Do While Mid(Str, j, 1) <> ")"
    If Mid(Str, j, 1) = ")" Then other_p = other_p - 1
    If Mid(Str, j, 1) = "(" Then other_p = other_p + 1
    j = j + 1
    Loop
    after = Mid(Str, i + 1, j - i)
    Else
    j = i + 1
    Do While (Mid(Str, j, 1) <> "+" And Mid(Str, j, 1) <> "-" And Mid(Str, j, 1) <> "*" And Mid(Str, j, 1) <> "/" And Mid(Str, j, 1) <> "," And Mid(Str, j, 1) <> ")")
    j = j + 1
    If j = Len(Str) + 1 Then Exit Do
    Loop
    after = Mid(Str, i + 1, j - i - 1)
    End If

    all = before & "^" & after
    Str = Replace(Str, all, String.Format(PowerString, before, after))
    i = 1
    End If
    i = i + 1
    End While
    Return Str
    End Function


    My issues lies with performance, as it appears that this function is the bottleneck of the entire application. How could I improve?







    share|improve this question























      up vote
      3
      down vote

      favorite









      up vote
      3
      down vote

      favorite











      The application uses NCalc to execute diverse calculations.



      The 'natural' way to write powers using a computer is using the ^ symbol. However, NCalc already uses it as bitwise or.



      I use the following function to parse calculations and convert it to the Pow(a,b) format.



       Private Function _replacePower(Str As String) As String
      Const PowerString As String = "Pow(0,1)"

      Dim i, j As Integer
      Dim c As String
      Dim before, after, all As String

      Dim other_p As Integer 'Keep track of other opening/closing parenthesis, to avoid breaking for a nested one.

      other_p = -1
      i = 1
      While i <= Len(Str)
      c = Mid(Str, i, 1)
      If c = "^" Then
      If Mid(Str, i - 1, 1) = ")" Then
      j = i - 1
      Do While Mid(Str, j, 1) <> "(" Or other_p > 0
      If Mid(Str, j, 1) = ")" Then other_p = other_p + 1
      If Mid(Str, j, 1) = "(" Then other_p = other_p - 1
      j = j - 1
      Loop
      before = Mid(Str, j, i - j)
      Else
      j = i - 1
      'The expression to be raised is everything between the power and + - * / , ( <- Opening parenthesis is not ok if there is no closing one, and this case is handled above.
      Do While (Mid(Str, j, 1) <> "+" And Mid(Str, j, 1) <> "-" And Mid(Str, j, 1) <> "*" And Mid(Str, j, 1) <> "/" And Mid(Str, j, 1) <> "," And Mid(Str, j, 1) <> "(")
      j = j - 1
      If j = 0 Then Exit Do
      Loop
      before = Mid(Str, j + 1, i - j - 1)
      End If

      other_p = -1

      If Mid(Str, i + 1, 1) = "(" Or other_p > 0 Then
      j = i + 1
      Do While Mid(Str, j, 1) <> ")"
      If Mid(Str, j, 1) = ")" Then other_p = other_p - 1
      If Mid(Str, j, 1) = "(" Then other_p = other_p + 1
      j = j + 1
      Loop
      after = Mid(Str, i + 1, j - i)
      Else
      j = i + 1
      Do While (Mid(Str, j, 1) <> "+" And Mid(Str, j, 1) <> "-" And Mid(Str, j, 1) <> "*" And Mid(Str, j, 1) <> "/" And Mid(Str, j, 1) <> "," And Mid(Str, j, 1) <> ")")
      j = j + 1
      If j = Len(Str) + 1 Then Exit Do
      Loop
      after = Mid(Str, i + 1, j - i - 1)
      End If

      all = before & "^" & after
      Str = Replace(Str, all, String.Format(PowerString, before, after))
      i = 1
      End If
      i = i + 1
      End While
      Return Str
      End Function


      My issues lies with performance, as it appears that this function is the bottleneck of the entire application. How could I improve?







      share|improve this question













      The application uses NCalc to execute diverse calculations.



      The 'natural' way to write powers using a computer is using the ^ symbol. However, NCalc already uses it as bitwise or.



      I use the following function to parse calculations and convert it to the Pow(a,b) format.



       Private Function _replacePower(Str As String) As String
      Const PowerString As String = "Pow(0,1)"

      Dim i, j As Integer
      Dim c As String
      Dim before, after, all As String

      Dim other_p As Integer 'Keep track of other opening/closing parenthesis, to avoid breaking for a nested one.

      other_p = -1
      i = 1
      While i <= Len(Str)
      c = Mid(Str, i, 1)
      If c = "^" Then
      If Mid(Str, i - 1, 1) = ")" Then
      j = i - 1
      Do While Mid(Str, j, 1) <> "(" Or other_p > 0
      If Mid(Str, j, 1) = ")" Then other_p = other_p + 1
      If Mid(Str, j, 1) = "(" Then other_p = other_p - 1
      j = j - 1
      Loop
      before = Mid(Str, j, i - j)
      Else
      j = i - 1
      'The expression to be raised is everything between the power and + - * / , ( <- Opening parenthesis is not ok if there is no closing one, and this case is handled above.
      Do While (Mid(Str, j, 1) <> "+" And Mid(Str, j, 1) <> "-" And Mid(Str, j, 1) <> "*" And Mid(Str, j, 1) <> "/" And Mid(Str, j, 1) <> "," And Mid(Str, j, 1) <> "(")
      j = j - 1
      If j = 0 Then Exit Do
      Loop
      before = Mid(Str, j + 1, i - j - 1)
      End If

      other_p = -1

      If Mid(Str, i + 1, 1) = "(" Or other_p > 0 Then
      j = i + 1
      Do While Mid(Str, j, 1) <> ")"
      If Mid(Str, j, 1) = ")" Then other_p = other_p - 1
      If Mid(Str, j, 1) = "(" Then other_p = other_p + 1
      j = j + 1
      Loop
      after = Mid(Str, i + 1, j - i)
      Else
      j = i + 1
      Do While (Mid(Str, j, 1) <> "+" And Mid(Str, j, 1) <> "-" And Mid(Str, j, 1) <> "*" And Mid(Str, j, 1) <> "/" And Mid(Str, j, 1) <> "," And Mid(Str, j, 1) <> ")")
      j = j + 1
      If j = Len(Str) + 1 Then Exit Do
      Loop
      after = Mid(Str, i + 1, j - i - 1)
      End If

      all = before & "^" & after
      Str = Replace(Str, all, String.Format(PowerString, before, after))
      i = 1
      End If
      i = i + 1
      End While
      Return Str
      End Function


      My issues lies with performance, as it appears that this function is the bottleneck of the entire application. How could I improve?









      share|improve this question












      share|improve this question




      share|improve this question








      edited Jul 18 at 7:01









      200_success

      123k14143399




      123k14143399









      asked Jul 18 at 6:49









      Maxime

      25419




      25419




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          5
          down vote



          accepted










          One of your biggest performance issues is, using Mid to get one character from the string. This gets the character as a string which is inefficient. It would be better to get the character as a Char, by using the index operator of the String class(Str(j)). To compare with literal characters use the character indicator(c)at the end of the string quotes("+"c).



          Whenever you have code duplication try to find a way to break that code out into a separate function. A case in point is when you are checking for operators in your string a simple function:



          Private Function isOperator(chr As Char) As Boolean
          Return chr = "+"c OrElse chr = "-"c OrElse chr = "*"c OrElse chr = "/"c OrElse chr = ","c OrElse chr = ")"c
          End Function


          simplifies and unclutters your main code.



          Whenever you have chained conditionals as above, use the shortcut boolean operators(OrElse and AndAlso). This way a false result will return right away instead of reading the rest.



          Don't use Len() to get the length of the string. The String class has a length property that you can use and put into a variable.



          Without any concrete examples of the complexity of the formulas that you're expecting, it's difficult to critique the efficiency of your approach. I'm thinking though, that as a general rule, anytime you are doing extensive string manipulations you should definitely look at using a StringBuilder.



          Simplified with these recommendations your code would look something like this:



          Private Function _replacePower(Str As String) As String
          Const PowerString As String = "Pow(0,1)"

          Dim i, j As Integer
          Dim c As Char
          Dim before, after, all As String
          Dim length As Integer = Str.Length
          Dim other_p As Integer 'Keep track of other opening/closing parenthesis, to avoid breaking for a nested one.

          other_p = -1
          i = 1
          While i <= length
          c = Str(i)
          If c = "^"c Then
          If Str(i - 1) = ")"c Then
          j = i - 1
          Do While Str(j) <> "("c Or other_p > 0
          If Str(j) = ")"c Then other_p = other_p + 1
          If Str(j) = "("c Then other_p = other_p - 1
          j = j - 1
          Loop
          before = Mid(Str, j, i - j)
          Else
          j = i - 1
          'The expression to be raised is everything between the power and + - * / , ( <- Opening parenthesis is not ok if there is no closing one, and this case is handled above.
          Do While (Not isOperator(Str(j)))
          j = j - 1
          If j = 0 Then Exit Do
          Loop
          before = Mid(Str, j + 1, i - j - 1)
          End If

          other_p = -1

          If Str(i + 1) = "("c Or other_p > 0 Then
          j = i + 1
          Do While Str(j) <> ")"c
          If Str(j) = ")"c Then other_p = other_p - 1
          If Str(j) = "("c Then other_p = other_p + 1
          j = j + 1
          Loop
          after = Mid(Str, i + 1, j - i)
          Else
          j = i + 1
          Do While (Not isOperator(Str(j)))
          j = j + 1
          If j = length + 1 Then Exit Do
          Loop
          after = Mid(Str, i + 1, j - i - 1)
          End If

          all = before & "^" & after
          Str = Replace(Str, all, String.Format(PowerString, before, after))
          i = 1
          End If
          i = i + 1
          End While
          Return Str
          End Function

          Private Function isOperator(chr As Char) As Boolean
          Return chr = "+"c OrElse chr = "-"c OrElse chr = "*"c OrElse chr = "/"c OrElse chr = ","c OrElse chr = ")"c
          End Function


          I took a more in depth look at your code, and realized there were quite a few more improvements that could be made.



          Don't be afraid to be verbose with your variable names. If you're worried about extra typing, Intellisense will bring up suggestions as you type, allowing you to just tab over the suggestion you want, instead of typing it all out.



          You seem to do a lot of checking for different types of characters. Really all you need to check for is digits and brackets.



          When checking for matching brackets, it is useful to have a `Dictionary(Of, Char, Integer)'. Opens are +1, closings are -1.



          The Char class has an IsDigit function that simplifies checking for those.



          There is another large code duplication when you check to the left and the right of the caret to get the appropriate strings.



          You didn't mention whether there is a possibility of more than one caret symbol in a string. It seems logical that this could be a possibility. My though is make the function recursive to keep checking the returned string for caret's.



          Putting this all together, you might come up with something like this:



          Private Function FixString(value As String) As String
          Dim caretIndex = 0
          caretIndex = value.IndexOf("^"c)
          If caretIndex <> -1 Then
          Dim sb As New StringBuilder()
          Dim leftHand = ""
          Dim rightHand = ""
          Dim leftHandIndex = 0
          Dim rightHandIndex = 0
          leftHandIndex = caretIndex - 1
          rightHandIndex = caretIndex + 1
          leftHand = getString(value, caretIndex, leftHandIndex, -1)
          rightHand = getString(value, caretIndex, rightHandIndex, 1)
          sb.AppendFormat("0Pow(1,2)3", value.Substring(0, leftHandIndex + 1), leftHand, rightHand, value.Substring(rightHandIndex))
          Return FixString(sb.ToString)
          Else
          Return value
          End If
          End Function

          Private Function getString(value As String, caretIndex As Integer, ByRef offset As Integer, increment As Integer) As String
          Dim bracketTotal = 0
          Dim index = offset
          If Char.IsDigit(value(index)) Then
          While index <> value.Length AndAlso Char.IsDigit(value(index))
          index += increment
          End While
          ElseIf brackets.ContainsKey(value(index)) Then
          bracketTotal += BRACKETS(value(index))
          index += increment
          While index <> value.Length AndAlso bracketTotal <> 0
          If BRACKETS.ContainsKey(value(index)) Then
          bracketTotal += BRACKETS(value(index))
          End If
          index += increment
          End While
          End If
          offset = index
          If increment = -1 Then
          Return value.Substring(index + 1, caretIndex - index - 1)
          End If
          Return value.Substring(caretIndex + 1, index - caretIndex - 1)
          End Function


          Private ReadOnly BRACKETS As Dictionary(Of Char, Integer) = New Dictionary(Of Char, Integer)() From

          ")"c, -1,
          "("c, 1



          I didn't see anything in your code, that would suggest the possibility of badly formatted strings, so I assumed that validation was being handled elsewhere.






          share|improve this answer























          • Just out of curiosity, what does the c-suffix mean in this expression: ")"c? Is this the same as using single quotes in C#?
            – t3chb0t
            Jul 18 at 17:11







          • 1




            @t3chb0t - Yes. In VB.net single quotes are for comments, so they use the c suffix to denote characters.
            – tinstaafl
            Jul 18 at 17:13











          • isOperator is painful. "+-*/,)".Contains(chr) is probably better - with an additional comment to explain why the expected ( is missing.
            – Gerrit0
            Jul 18 at 22:40










          • Yes, so painful I dropped it. It was mostly to make a point in any case.
            – tinstaafl
            Jul 18 at 22:44










          • Thank you for this, it is very helpful for this project, and in general. Just a detail, in the final code, you declare (left|right)BracketTotal that are not used in the code.
            – Maxime
            Jul 19 at 7:14










          Your Answer




          StackExchange.ifUsing("editor", function ()
          return StackExchange.using("mathjaxEditing", function ()
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          );
          );
          , "mathjax-editing");

          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "196"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );








           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f199725%2freplace-ab-by-powa-b%23new-answer', 'question_page');

          );

          Post as a guest






























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          5
          down vote



          accepted










          One of your biggest performance issues is, using Mid to get one character from the string. This gets the character as a string which is inefficient. It would be better to get the character as a Char, by using the index operator of the String class(Str(j)). To compare with literal characters use the character indicator(c)at the end of the string quotes("+"c).



          Whenever you have code duplication try to find a way to break that code out into a separate function. A case in point is when you are checking for operators in your string a simple function:



          Private Function isOperator(chr As Char) As Boolean
          Return chr = "+"c OrElse chr = "-"c OrElse chr = "*"c OrElse chr = "/"c OrElse chr = ","c OrElse chr = ")"c
          End Function


          simplifies and unclutters your main code.



          Whenever you have chained conditionals as above, use the shortcut boolean operators(OrElse and AndAlso). This way a false result will return right away instead of reading the rest.



          Don't use Len() to get the length of the string. The String class has a length property that you can use and put into a variable.



          Without any concrete examples of the complexity of the formulas that you're expecting, it's difficult to critique the efficiency of your approach. I'm thinking though, that as a general rule, anytime you are doing extensive string manipulations you should definitely look at using a StringBuilder.



          Simplified with these recommendations your code would look something like this:



          Private Function _replacePower(Str As String) As String
          Const PowerString As String = "Pow(0,1)"

          Dim i, j As Integer
          Dim c As Char
          Dim before, after, all As String
          Dim length As Integer = Str.Length
          Dim other_p As Integer 'Keep track of other opening/closing parenthesis, to avoid breaking for a nested one.

          other_p = -1
          i = 1
          While i <= length
          c = Str(i)
          If c = "^"c Then
          If Str(i - 1) = ")"c Then
          j = i - 1
          Do While Str(j) <> "("c Or other_p > 0
          If Str(j) = ")"c Then other_p = other_p + 1
          If Str(j) = "("c Then other_p = other_p - 1
          j = j - 1
          Loop
          before = Mid(Str, j, i - j)
          Else
          j = i - 1
          'The expression to be raised is everything between the power and + - * / , ( <- Opening parenthesis is not ok if there is no closing one, and this case is handled above.
          Do While (Not isOperator(Str(j)))
          j = j - 1
          If j = 0 Then Exit Do
          Loop
          before = Mid(Str, j + 1, i - j - 1)
          End If

          other_p = -1

          If Str(i + 1) = "("c Or other_p > 0 Then
          j = i + 1
          Do While Str(j) <> ")"c
          If Str(j) = ")"c Then other_p = other_p - 1
          If Str(j) = "("c Then other_p = other_p + 1
          j = j + 1
          Loop
          after = Mid(Str, i + 1, j - i)
          Else
          j = i + 1
          Do While (Not isOperator(Str(j)))
          j = j + 1
          If j = length + 1 Then Exit Do
          Loop
          after = Mid(Str, i + 1, j - i - 1)
          End If

          all = before & "^" & after
          Str = Replace(Str, all, String.Format(PowerString, before, after))
          i = 1
          End If
          i = i + 1
          End While
          Return Str
          End Function

          Private Function isOperator(chr As Char) As Boolean
          Return chr = "+"c OrElse chr = "-"c OrElse chr = "*"c OrElse chr = "/"c OrElse chr = ","c OrElse chr = ")"c
          End Function


          I took a more in depth look at your code, and realized there were quite a few more improvements that could be made.



          Don't be afraid to be verbose with your variable names. If you're worried about extra typing, Intellisense will bring up suggestions as you type, allowing you to just tab over the suggestion you want, instead of typing it all out.



          You seem to do a lot of checking for different types of characters. Really all you need to check for is digits and brackets.



          When checking for matching brackets, it is useful to have a `Dictionary(Of, Char, Integer)'. Opens are +1, closings are -1.



          The Char class has an IsDigit function that simplifies checking for those.



          There is another large code duplication when you check to the left and the right of the caret to get the appropriate strings.



          You didn't mention whether there is a possibility of more than one caret symbol in a string. It seems logical that this could be a possibility. My though is make the function recursive to keep checking the returned string for caret's.



          Putting this all together, you might come up with something like this:



          Private Function FixString(value As String) As String
          Dim caretIndex = 0
          caretIndex = value.IndexOf("^"c)
          If caretIndex <> -1 Then
          Dim sb As New StringBuilder()
          Dim leftHand = ""
          Dim rightHand = ""
          Dim leftHandIndex = 0
          Dim rightHandIndex = 0
          leftHandIndex = caretIndex - 1
          rightHandIndex = caretIndex + 1
          leftHand = getString(value, caretIndex, leftHandIndex, -1)
          rightHand = getString(value, caretIndex, rightHandIndex, 1)
          sb.AppendFormat("0Pow(1,2)3", value.Substring(0, leftHandIndex + 1), leftHand, rightHand, value.Substring(rightHandIndex))
          Return FixString(sb.ToString)
          Else
          Return value
          End If
          End Function

          Private Function getString(value As String, caretIndex As Integer, ByRef offset As Integer, increment As Integer) As String
          Dim bracketTotal = 0
          Dim index = offset
          If Char.IsDigit(value(index)) Then
          While index <> value.Length AndAlso Char.IsDigit(value(index))
          index += increment
          End While
          ElseIf brackets.ContainsKey(value(index)) Then
          bracketTotal += BRACKETS(value(index))
          index += increment
          While index <> value.Length AndAlso bracketTotal <> 0
          If BRACKETS.ContainsKey(value(index)) Then
          bracketTotal += BRACKETS(value(index))
          End If
          index += increment
          End While
          End If
          offset = index
          If increment = -1 Then
          Return value.Substring(index + 1, caretIndex - index - 1)
          End If
          Return value.Substring(caretIndex + 1, index - caretIndex - 1)
          End Function


          Private ReadOnly BRACKETS As Dictionary(Of Char, Integer) = New Dictionary(Of Char, Integer)() From

          ")"c, -1,
          "("c, 1



          I didn't see anything in your code, that would suggest the possibility of badly formatted strings, so I assumed that validation was being handled elsewhere.






          share|improve this answer























          • Just out of curiosity, what does the c-suffix mean in this expression: ")"c? Is this the same as using single quotes in C#?
            – t3chb0t
            Jul 18 at 17:11







          • 1




            @t3chb0t - Yes. In VB.net single quotes are for comments, so they use the c suffix to denote characters.
            – tinstaafl
            Jul 18 at 17:13











          • isOperator is painful. "+-*/,)".Contains(chr) is probably better - with an additional comment to explain why the expected ( is missing.
            – Gerrit0
            Jul 18 at 22:40










          • Yes, so painful I dropped it. It was mostly to make a point in any case.
            – tinstaafl
            Jul 18 at 22:44










          • Thank you for this, it is very helpful for this project, and in general. Just a detail, in the final code, you declare (left|right)BracketTotal that are not used in the code.
            – Maxime
            Jul 19 at 7:14














          up vote
          5
          down vote



          accepted










          One of your biggest performance issues is, using Mid to get one character from the string. This gets the character as a string which is inefficient. It would be better to get the character as a Char, by using the index operator of the String class(Str(j)). To compare with literal characters use the character indicator(c)at the end of the string quotes("+"c).



          Whenever you have code duplication try to find a way to break that code out into a separate function. A case in point is when you are checking for operators in your string a simple function:



          Private Function isOperator(chr As Char) As Boolean
          Return chr = "+"c OrElse chr = "-"c OrElse chr = "*"c OrElse chr = "/"c OrElse chr = ","c OrElse chr = ")"c
          End Function


          simplifies and unclutters your main code.



          Whenever you have chained conditionals as above, use the shortcut boolean operators(OrElse and AndAlso). This way a false result will return right away instead of reading the rest.



          Don't use Len() to get the length of the string. The String class has a length property that you can use and put into a variable.



          Without any concrete examples of the complexity of the formulas that you're expecting, it's difficult to critique the efficiency of your approach. I'm thinking though, that as a general rule, anytime you are doing extensive string manipulations you should definitely look at using a StringBuilder.



          Simplified with these recommendations your code would look something like this:



          Private Function _replacePower(Str As String) As String
          Const PowerString As String = "Pow(0,1)"

          Dim i, j As Integer
          Dim c As Char
          Dim before, after, all As String
          Dim length As Integer = Str.Length
          Dim other_p As Integer 'Keep track of other opening/closing parenthesis, to avoid breaking for a nested one.

          other_p = -1
          i = 1
          While i <= length
          c = Str(i)
          If c = "^"c Then
          If Str(i - 1) = ")"c Then
          j = i - 1
          Do While Str(j) <> "("c Or other_p > 0
          If Str(j) = ")"c Then other_p = other_p + 1
          If Str(j) = "("c Then other_p = other_p - 1
          j = j - 1
          Loop
          before = Mid(Str, j, i - j)
          Else
          j = i - 1
          'The expression to be raised is everything between the power and + - * / , ( <- Opening parenthesis is not ok if there is no closing one, and this case is handled above.
          Do While (Not isOperator(Str(j)))
          j = j - 1
          If j = 0 Then Exit Do
          Loop
          before = Mid(Str, j + 1, i - j - 1)
          End If

          other_p = -1

          If Str(i + 1) = "("c Or other_p > 0 Then
          j = i + 1
          Do While Str(j) <> ")"c
          If Str(j) = ")"c Then other_p = other_p - 1
          If Str(j) = "("c Then other_p = other_p + 1
          j = j + 1
          Loop
          after = Mid(Str, i + 1, j - i)
          Else
          j = i + 1
          Do While (Not isOperator(Str(j)))
          j = j + 1
          If j = length + 1 Then Exit Do
          Loop
          after = Mid(Str, i + 1, j - i - 1)
          End If

          all = before & "^" & after
          Str = Replace(Str, all, String.Format(PowerString, before, after))
          i = 1
          End If
          i = i + 1
          End While
          Return Str
          End Function

          Private Function isOperator(chr As Char) As Boolean
          Return chr = "+"c OrElse chr = "-"c OrElse chr = "*"c OrElse chr = "/"c OrElse chr = ","c OrElse chr = ")"c
          End Function


          I took a more in depth look at your code, and realized there were quite a few more improvements that could be made.



          Don't be afraid to be verbose with your variable names. If you're worried about extra typing, Intellisense will bring up suggestions as you type, allowing you to just tab over the suggestion you want, instead of typing it all out.



          You seem to do a lot of checking for different types of characters. Really all you need to check for is digits and brackets.



          When checking for matching brackets, it is useful to have a `Dictionary(Of, Char, Integer)'. Opens are +1, closings are -1.



          The Char class has an IsDigit function that simplifies checking for those.



          There is another large code duplication when you check to the left and the right of the caret to get the appropriate strings.



          You didn't mention whether there is a possibility of more than one caret symbol in a string. It seems logical that this could be a possibility. My though is make the function recursive to keep checking the returned string for caret's.



          Putting this all together, you might come up with something like this:



          Private Function FixString(value As String) As String
          Dim caretIndex = 0
          caretIndex = value.IndexOf("^"c)
          If caretIndex <> -1 Then
          Dim sb As New StringBuilder()
          Dim leftHand = ""
          Dim rightHand = ""
          Dim leftHandIndex = 0
          Dim rightHandIndex = 0
          leftHandIndex = caretIndex - 1
          rightHandIndex = caretIndex + 1
          leftHand = getString(value, caretIndex, leftHandIndex, -1)
          rightHand = getString(value, caretIndex, rightHandIndex, 1)
          sb.AppendFormat("0Pow(1,2)3", value.Substring(0, leftHandIndex + 1), leftHand, rightHand, value.Substring(rightHandIndex))
          Return FixString(sb.ToString)
          Else
          Return value
          End If
          End Function

          Private Function getString(value As String, caretIndex As Integer, ByRef offset As Integer, increment As Integer) As String
          Dim bracketTotal = 0
          Dim index = offset
          If Char.IsDigit(value(index)) Then
          While index <> value.Length AndAlso Char.IsDigit(value(index))
          index += increment
          End While
          ElseIf brackets.ContainsKey(value(index)) Then
          bracketTotal += BRACKETS(value(index))
          index += increment
          While index <> value.Length AndAlso bracketTotal <> 0
          If BRACKETS.ContainsKey(value(index)) Then
          bracketTotal += BRACKETS(value(index))
          End If
          index += increment
          End While
          End If
          offset = index
          If increment = -1 Then
          Return value.Substring(index + 1, caretIndex - index - 1)
          End If
          Return value.Substring(caretIndex + 1, index - caretIndex - 1)
          End Function


          Private ReadOnly BRACKETS As Dictionary(Of Char, Integer) = New Dictionary(Of Char, Integer)() From

          ")"c, -1,
          "("c, 1



          I didn't see anything in your code, that would suggest the possibility of badly formatted strings, so I assumed that validation was being handled elsewhere.






          share|improve this answer























          • Just out of curiosity, what does the c-suffix mean in this expression: ")"c? Is this the same as using single quotes in C#?
            – t3chb0t
            Jul 18 at 17:11







          • 1




            @t3chb0t - Yes. In VB.net single quotes are for comments, so they use the c suffix to denote characters.
            – tinstaafl
            Jul 18 at 17:13











          • isOperator is painful. "+-*/,)".Contains(chr) is probably better - with an additional comment to explain why the expected ( is missing.
            – Gerrit0
            Jul 18 at 22:40










          • Yes, so painful I dropped it. It was mostly to make a point in any case.
            – tinstaafl
            Jul 18 at 22:44










          • Thank you for this, it is very helpful for this project, and in general. Just a detail, in the final code, you declare (left|right)BracketTotal that are not used in the code.
            – Maxime
            Jul 19 at 7:14












          up vote
          5
          down vote



          accepted







          up vote
          5
          down vote



          accepted






          One of your biggest performance issues is, using Mid to get one character from the string. This gets the character as a string which is inefficient. It would be better to get the character as a Char, by using the index operator of the String class(Str(j)). To compare with literal characters use the character indicator(c)at the end of the string quotes("+"c).



          Whenever you have code duplication try to find a way to break that code out into a separate function. A case in point is when you are checking for operators in your string a simple function:



          Private Function isOperator(chr As Char) As Boolean
          Return chr = "+"c OrElse chr = "-"c OrElse chr = "*"c OrElse chr = "/"c OrElse chr = ","c OrElse chr = ")"c
          End Function


          simplifies and unclutters your main code.



          Whenever you have chained conditionals as above, use the shortcut boolean operators(OrElse and AndAlso). This way a false result will return right away instead of reading the rest.



          Don't use Len() to get the length of the string. The String class has a length property that you can use and put into a variable.



          Without any concrete examples of the complexity of the formulas that you're expecting, it's difficult to critique the efficiency of your approach. I'm thinking though, that as a general rule, anytime you are doing extensive string manipulations you should definitely look at using a StringBuilder.



          Simplified with these recommendations your code would look something like this:



          Private Function _replacePower(Str As String) As String
          Const PowerString As String = "Pow(0,1)"

          Dim i, j As Integer
          Dim c As Char
          Dim before, after, all As String
          Dim length As Integer = Str.Length
          Dim other_p As Integer 'Keep track of other opening/closing parenthesis, to avoid breaking for a nested one.

          other_p = -1
          i = 1
          While i <= length
          c = Str(i)
          If c = "^"c Then
          If Str(i - 1) = ")"c Then
          j = i - 1
          Do While Str(j) <> "("c Or other_p > 0
          If Str(j) = ")"c Then other_p = other_p + 1
          If Str(j) = "("c Then other_p = other_p - 1
          j = j - 1
          Loop
          before = Mid(Str, j, i - j)
          Else
          j = i - 1
          'The expression to be raised is everything between the power and + - * / , ( <- Opening parenthesis is not ok if there is no closing one, and this case is handled above.
          Do While (Not isOperator(Str(j)))
          j = j - 1
          If j = 0 Then Exit Do
          Loop
          before = Mid(Str, j + 1, i - j - 1)
          End If

          other_p = -1

          If Str(i + 1) = "("c Or other_p > 0 Then
          j = i + 1
          Do While Str(j) <> ")"c
          If Str(j) = ")"c Then other_p = other_p - 1
          If Str(j) = "("c Then other_p = other_p + 1
          j = j + 1
          Loop
          after = Mid(Str, i + 1, j - i)
          Else
          j = i + 1
          Do While (Not isOperator(Str(j)))
          j = j + 1
          If j = length + 1 Then Exit Do
          Loop
          after = Mid(Str, i + 1, j - i - 1)
          End If

          all = before & "^" & after
          Str = Replace(Str, all, String.Format(PowerString, before, after))
          i = 1
          End If
          i = i + 1
          End While
          Return Str
          End Function

          Private Function isOperator(chr As Char) As Boolean
          Return chr = "+"c OrElse chr = "-"c OrElse chr = "*"c OrElse chr = "/"c OrElse chr = ","c OrElse chr = ")"c
          End Function


          I took a more in depth look at your code, and realized there were quite a few more improvements that could be made.



          Don't be afraid to be verbose with your variable names. If you're worried about extra typing, Intellisense will bring up suggestions as you type, allowing you to just tab over the suggestion you want, instead of typing it all out.



          You seem to do a lot of checking for different types of characters. Really all you need to check for is digits and brackets.



          When checking for matching brackets, it is useful to have a `Dictionary(Of, Char, Integer)'. Opens are +1, closings are -1.



          The Char class has an IsDigit function that simplifies checking for those.



          There is another large code duplication when you check to the left and the right of the caret to get the appropriate strings.



          You didn't mention whether there is a possibility of more than one caret symbol in a string. It seems logical that this could be a possibility. My though is make the function recursive to keep checking the returned string for caret's.



          Putting this all together, you might come up with something like this:



          Private Function FixString(value As String) As String
          Dim caretIndex = 0
          caretIndex = value.IndexOf("^"c)
          If caretIndex <> -1 Then
          Dim sb As New StringBuilder()
          Dim leftHand = ""
          Dim rightHand = ""
          Dim leftHandIndex = 0
          Dim rightHandIndex = 0
          leftHandIndex = caretIndex - 1
          rightHandIndex = caretIndex + 1
          leftHand = getString(value, caretIndex, leftHandIndex, -1)
          rightHand = getString(value, caretIndex, rightHandIndex, 1)
          sb.AppendFormat("0Pow(1,2)3", value.Substring(0, leftHandIndex + 1), leftHand, rightHand, value.Substring(rightHandIndex))
          Return FixString(sb.ToString)
          Else
          Return value
          End If
          End Function

          Private Function getString(value As String, caretIndex As Integer, ByRef offset As Integer, increment As Integer) As String
          Dim bracketTotal = 0
          Dim index = offset
          If Char.IsDigit(value(index)) Then
          While index <> value.Length AndAlso Char.IsDigit(value(index))
          index += increment
          End While
          ElseIf brackets.ContainsKey(value(index)) Then
          bracketTotal += BRACKETS(value(index))
          index += increment
          While index <> value.Length AndAlso bracketTotal <> 0
          If BRACKETS.ContainsKey(value(index)) Then
          bracketTotal += BRACKETS(value(index))
          End If
          index += increment
          End While
          End If
          offset = index
          If increment = -1 Then
          Return value.Substring(index + 1, caretIndex - index - 1)
          End If
          Return value.Substring(caretIndex + 1, index - caretIndex - 1)
          End Function


          Private ReadOnly BRACKETS As Dictionary(Of Char, Integer) = New Dictionary(Of Char, Integer)() From

          ")"c, -1,
          "("c, 1



          I didn't see anything in your code, that would suggest the possibility of badly formatted strings, so I assumed that validation was being handled elsewhere.






          share|improve this answer















          One of your biggest performance issues is, using Mid to get one character from the string. This gets the character as a string which is inefficient. It would be better to get the character as a Char, by using the index operator of the String class(Str(j)). To compare with literal characters use the character indicator(c)at the end of the string quotes("+"c).



          Whenever you have code duplication try to find a way to break that code out into a separate function. A case in point is when you are checking for operators in your string a simple function:



          Private Function isOperator(chr As Char) As Boolean
          Return chr = "+"c OrElse chr = "-"c OrElse chr = "*"c OrElse chr = "/"c OrElse chr = ","c OrElse chr = ")"c
          End Function


          simplifies and unclutters your main code.



          Whenever you have chained conditionals as above, use the shortcut boolean operators(OrElse and AndAlso). This way a false result will return right away instead of reading the rest.



          Don't use Len() to get the length of the string. The String class has a length property that you can use and put into a variable.



          Without any concrete examples of the complexity of the formulas that you're expecting, it's difficult to critique the efficiency of your approach. I'm thinking though, that as a general rule, anytime you are doing extensive string manipulations you should definitely look at using a StringBuilder.



          Simplified with these recommendations your code would look something like this:



          Private Function _replacePower(Str As String) As String
          Const PowerString As String = "Pow(0,1)"

          Dim i, j As Integer
          Dim c As Char
          Dim before, after, all As String
          Dim length As Integer = Str.Length
          Dim other_p As Integer 'Keep track of other opening/closing parenthesis, to avoid breaking for a nested one.

          other_p = -1
          i = 1
          While i <= length
          c = Str(i)
          If c = "^"c Then
          If Str(i - 1) = ")"c Then
          j = i - 1
          Do While Str(j) <> "("c Or other_p > 0
          If Str(j) = ")"c Then other_p = other_p + 1
          If Str(j) = "("c Then other_p = other_p - 1
          j = j - 1
          Loop
          before = Mid(Str, j, i - j)
          Else
          j = i - 1
          'The expression to be raised is everything between the power and + - * / , ( <- Opening parenthesis is not ok if there is no closing one, and this case is handled above.
          Do While (Not isOperator(Str(j)))
          j = j - 1
          If j = 0 Then Exit Do
          Loop
          before = Mid(Str, j + 1, i - j - 1)
          End If

          other_p = -1

          If Str(i + 1) = "("c Or other_p > 0 Then
          j = i + 1
          Do While Str(j) <> ")"c
          If Str(j) = ")"c Then other_p = other_p - 1
          If Str(j) = "("c Then other_p = other_p + 1
          j = j + 1
          Loop
          after = Mid(Str, i + 1, j - i)
          Else
          j = i + 1
          Do While (Not isOperator(Str(j)))
          j = j + 1
          If j = length + 1 Then Exit Do
          Loop
          after = Mid(Str, i + 1, j - i - 1)
          End If

          all = before & "^" & after
          Str = Replace(Str, all, String.Format(PowerString, before, after))
          i = 1
          End If
          i = i + 1
          End While
          Return Str
          End Function

          Private Function isOperator(chr As Char) As Boolean
          Return chr = "+"c OrElse chr = "-"c OrElse chr = "*"c OrElse chr = "/"c OrElse chr = ","c OrElse chr = ")"c
          End Function


          I took a more in depth look at your code, and realized there were quite a few more improvements that could be made.



          Don't be afraid to be verbose with your variable names. If you're worried about extra typing, Intellisense will bring up suggestions as you type, allowing you to just tab over the suggestion you want, instead of typing it all out.



          You seem to do a lot of checking for different types of characters. Really all you need to check for is digits and brackets.



          When checking for matching brackets, it is useful to have a `Dictionary(Of, Char, Integer)'. Opens are +1, closings are -1.



          The Char class has an IsDigit function that simplifies checking for those.



          There is another large code duplication when you check to the left and the right of the caret to get the appropriate strings.



          You didn't mention whether there is a possibility of more than one caret symbol in a string. It seems logical that this could be a possibility. My though is make the function recursive to keep checking the returned string for caret's.



          Putting this all together, you might come up with something like this:



          Private Function FixString(value As String) As String
          Dim caretIndex = 0
          caretIndex = value.IndexOf("^"c)
          If caretIndex <> -1 Then
          Dim sb As New StringBuilder()
          Dim leftHand = ""
          Dim rightHand = ""
          Dim leftHandIndex = 0
          Dim rightHandIndex = 0
          leftHandIndex = caretIndex - 1
          rightHandIndex = caretIndex + 1
          leftHand = getString(value, caretIndex, leftHandIndex, -1)
          rightHand = getString(value, caretIndex, rightHandIndex, 1)
          sb.AppendFormat("0Pow(1,2)3", value.Substring(0, leftHandIndex + 1), leftHand, rightHand, value.Substring(rightHandIndex))
          Return FixString(sb.ToString)
          Else
          Return value
          End If
          End Function

          Private Function getString(value As String, caretIndex As Integer, ByRef offset As Integer, increment As Integer) As String
          Dim bracketTotal = 0
          Dim index = offset
          If Char.IsDigit(value(index)) Then
          While index <> value.Length AndAlso Char.IsDigit(value(index))
          index += increment
          End While
          ElseIf brackets.ContainsKey(value(index)) Then
          bracketTotal += BRACKETS(value(index))
          index += increment
          While index <> value.Length AndAlso bracketTotal <> 0
          If BRACKETS.ContainsKey(value(index)) Then
          bracketTotal += BRACKETS(value(index))
          End If
          index += increment
          End While
          End If
          offset = index
          If increment = -1 Then
          Return value.Substring(index + 1, caretIndex - index - 1)
          End If
          Return value.Substring(caretIndex + 1, index - caretIndex - 1)
          End Function


          Private ReadOnly BRACKETS As Dictionary(Of Char, Integer) = New Dictionary(Of Char, Integer)() From

          ")"c, -1,
          "("c, 1



          I didn't see anything in your code, that would suggest the possibility of badly formatted strings, so I assumed that validation was being handled elsewhere.







          share|improve this answer















          share|improve this answer



          share|improve this answer








          edited Jul 19 at 9:33


























          answered Jul 18 at 15:08









          tinstaafl

          5,482625




          5,482625











          • Just out of curiosity, what does the c-suffix mean in this expression: ")"c? Is this the same as using single quotes in C#?
            – t3chb0t
            Jul 18 at 17:11







          • 1




            @t3chb0t - Yes. In VB.net single quotes are for comments, so they use the c suffix to denote characters.
            – tinstaafl
            Jul 18 at 17:13











          • isOperator is painful. "+-*/,)".Contains(chr) is probably better - with an additional comment to explain why the expected ( is missing.
            – Gerrit0
            Jul 18 at 22:40










          • Yes, so painful I dropped it. It was mostly to make a point in any case.
            – tinstaafl
            Jul 18 at 22:44










          • Thank you for this, it is very helpful for this project, and in general. Just a detail, in the final code, you declare (left|right)BracketTotal that are not used in the code.
            – Maxime
            Jul 19 at 7:14
















          • Just out of curiosity, what does the c-suffix mean in this expression: ")"c? Is this the same as using single quotes in C#?
            – t3chb0t
            Jul 18 at 17:11







          • 1




            @t3chb0t - Yes. In VB.net single quotes are for comments, so they use the c suffix to denote characters.
            – tinstaafl
            Jul 18 at 17:13











          • isOperator is painful. "+-*/,)".Contains(chr) is probably better - with an additional comment to explain why the expected ( is missing.
            – Gerrit0
            Jul 18 at 22:40










          • Yes, so painful I dropped it. It was mostly to make a point in any case.
            – tinstaafl
            Jul 18 at 22:44










          • Thank you for this, it is very helpful for this project, and in general. Just a detail, in the final code, you declare (left|right)BracketTotal that are not used in the code.
            – Maxime
            Jul 19 at 7:14















          Just out of curiosity, what does the c-suffix mean in this expression: ")"c? Is this the same as using single quotes in C#?
          – t3chb0t
          Jul 18 at 17:11





          Just out of curiosity, what does the c-suffix mean in this expression: ")"c? Is this the same as using single quotes in C#?
          – t3chb0t
          Jul 18 at 17:11





          1




          1




          @t3chb0t - Yes. In VB.net single quotes are for comments, so they use the c suffix to denote characters.
          – tinstaafl
          Jul 18 at 17:13





          @t3chb0t - Yes. In VB.net single quotes are for comments, so they use the c suffix to denote characters.
          – tinstaafl
          Jul 18 at 17:13













          isOperator is painful. "+-*/,)".Contains(chr) is probably better - with an additional comment to explain why the expected ( is missing.
          – Gerrit0
          Jul 18 at 22:40




          isOperator is painful. "+-*/,)".Contains(chr) is probably better - with an additional comment to explain why the expected ( is missing.
          – Gerrit0
          Jul 18 at 22:40












          Yes, so painful I dropped it. It was mostly to make a point in any case.
          – tinstaafl
          Jul 18 at 22:44




          Yes, so painful I dropped it. It was mostly to make a point in any case.
          – tinstaafl
          Jul 18 at 22:44












          Thank you for this, it is very helpful for this project, and in general. Just a detail, in the final code, you declare (left|right)BracketTotal that are not used in the code.
          – Maxime
          Jul 19 at 7:14




          Thank you for this, it is very helpful for this project, and in general. Just a detail, in the final code, you declare (left|right)BracketTotal that are not used in the code.
          – Maxime
          Jul 19 at 7:14












           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f199725%2freplace-ab-by-powa-b%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Python Lists

          Aion

          JavaScript Array Iteration Methods