Archive

Posts Tagged ‘String to Math’

!Reinventing the wheel

February 14, 2010 1 comment

While digging into the ways I might resolve the nested bracketing of expressions I did a bit of parallel homework and discovered that javax has functionality which can be leveraged to save a world of work here, the ScriptEngine interface. I played around with it for a while and came up with this example of how we might implement a calculator using the ScriptEngine.

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class Evaluator {
private ScriptEngineManager sm = new ScriptEngineManager();
private ScriptEngine sEngine = sm.getEngineByName("js");

public double stringEval(String expr)
{
Object res = "";
        try {
           res = sEngine.eval(expr);
          } 
         catch(ScriptException se) {
            se.printStackTrace();
        }
        return Double.parseDouble( res.toString());
}

} 

Here’s how we might use it:

Evaluator evr = new Evaluator();
String sTest = "+1+9*(2 * 5)";
double dd = evr.stringEval(sTest);
System.out.println(dd);

Not reinventing the wheel here proves to be a big win. It runs fast and significantly reduces the amount of code which needs to be supported. Some useful and broader based information on using scripting can be found here.

Improving the String math calculator

February 12, 2010 Leave a comment

In Reducing the Necronomicon to numbers I demonstrated how we might parse and evaluate a String reduced to and resulting in a simple mathematical expression which can be evaluated. I’ve given the matter a little more thought in the interim and extended and refactored the calculator a little so that it works on doubles, which I went for in preference to float on grounds of overall utility. As I indicated in the earlier article, for simple calculation all that really was required was to handle the decimal places, and, of course, and what I omitted to mention at the time, to overload the calc method so that it handled doubles and returned doubles.  So this is what the code looks like.


public double sbExtendedMathEvaluator(StringBuffer sb)
    {
    double result = 0.0;
    char operator = ' ';
    String tmp = "";

    char[] aEval = sb.toString().toCharArray();
    int last = aEval.length;

    for (int i = 0; i < last; i++)
    {

    if(!isNumOrDecPlace(aEval[i])  )
    {
    if (isOperator(aEval[i]))   { operator = aEval[i]; }
    tmp = ""; // reset tmp
    continue;
    }

    tmp += Character.toString(aEval[i]);
    if(i + 1 == last){result = calc(result,operator,toDouble(tmp)); break;}

    if(!isNumOrDecPlace(aEval[i + 1])  ){  result = calc(result,operator,toDouble(tmp));   }
    }
return result;
}

The new syntax helpers for this are

public boolean isNumOrDecPlace(char c)
    {
    if((isInt(c)) || (isDecimalPlace(c )))    {return true;}
    return false;    }

public double toDouble(String tmp)
    {
    return Double.parseDouble(tmp);
    }

and isNumOrDecPlace’s dependency

public boolean isDecimalPlace(char c)
    {
    if (c == '.'){return true;}
    return false;
    }

And the overloaded calc method now looks like:

public double calc(double result, char operator, double val)
    {
    if (operator == '+')
    {return result + val;}
    if (operator == '+')
    {return result - val;}
    if (operator == '*')
    {return result * val;}
    if (operator == '/')
    {return result / val;}
     if (operator == '%') // modulus
    {return result % val;}
    // no op possible? just throw back the result unmutated
    // but we do need to handle the exceptions.... we'll get to those later
    return result;
    }

This all works pretty much as intended, so I’d like to consider now some further possibilities and make this a fully functional String math evaluator, which I’ll be addressing in some future articles.

First, I need to test this into the ground so I’ll be running the class through JUnit and doing some mock testing with something like (and probably will be) EasyMock. It’s generally good practice to write your JUnits upfront btw, however as a lot of this was written by the 20 cups of coffee and “let’s code it and see if it works” discovery method it wasn’t really feasible in this case so I substituted a number of ad hoc tests to keep an eye on how the code was coming along. Since I work almost exclusively in NetBeans, retrofitting the JUnits is merely a question of hitting Tools->Create Unit tests and then writing the test evaluations anyway.

The we have some other issues to explore to make the evaluation relatively complete. We need to be able to handle bracketed expressions like ” 1 + ( 2 * 3 )”. At the moment if you try and evaluate this as is, it will return 6 which is not quite the result we want. It will also need to handle signed values appropriately – at the moment it doesn’t handle signed values terribly well (it ignores them) e.g. “+10 + -10 ” will give you 10.0 as a result, again not what is wanted. It would also be nice if it could handle some simple expressions like “pow” and “sqrt”. We also need to be able to handle the exceptions which emerge e.g. divide by zero errors, or erroneously unbalanced bracketed expressions such as ” 1 + (2 *5) + 4)”.

Later I’ve had a look at and a think about at the mechanisms for doing the bracketed expression analysis and it looks potentially nasty but my current implementation is not a million light years away from what’s required to resolve this. I just need one of those much-vaunted sudden moments of absolute clarity to mop this one up! Wikipedia has the full lowdown on what’s needed here, and this is potentially non-trivial.

Reducing the Necronomicon to numbers….

February 10, 2010 2 comments

Abdul Alhazred, in his crazed and infinite wisdom, besides being the author of the Necronomicon was also something of a part time numerologist. Between inscribing in blood (derived from virgins, a scarce commodity in the circles in which Abdul moved) the memorial pages which have driven many beyond the brink of sanity, he spent much time in contemplating how one might reductively turn language into numbers. One method which he overlooked was that proposed by Mr Donovan K. Loucks. It appeared as a comment to an earlier post on “Counting vowels… the faster AND dynamically reusable way…” and in essence what we need to do here is take a given String, remove non-vowels, i.e. consonants, punctuation and whitespace, replace the vowels with “+1” and suffix a “0” to the end so that the final number to be added will be a ten.

The replacement is easy enough using the locateStringContents method set out earlier, which we’ll bolt into a putative new class called LovecraftEvaluator, along with the boringly necessary toIntArray method which appears more times on this blog than Cthulhu has yawned in the sleeping millenia. We don’t actually need to replace anything at all, given that we are just interested in the vowels, so we’ll just create a new StringBuffer and for each vowel append a “+1” and StringBuffer.join a “0” once this is complete. Then we have to calculate the sum of these numbers….


String test = "The Necronomicon does not make for light reading. At all.";
LovecraftEvaluator le = new LovecraftEvaluator();
String vowels = "aeiouAEIOU";
int[] positions = le.locateStringContents(new StringBuffer(test), new StringBuffer(vowels));

int ctr = 0;
StringBuffer sOut = new StringBuffer();
for (int i = 0; i < positions.length; i++)
{
sOut.append("+1");
}
sOut.append(0);

If we print the contents of sOut we’ll get this:

+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+10

Now we’re onto phase 2, evaluating the String mathematically. Java still doesn’t have closures or lambdas, even though they’ve been on the request list since the early middle ages. Apparently they’re proposed for JDK 7 but I won’t be holding my breath; something we’ve taken for granted since year 1 in Ruby is still unimplemented in Java and likely to remain so in the near future.

Aside from the obvious occult usages of being able to reduce Strings to their numerical values, there is a serious method behind this which will allow you to calculate a mathematically expressed String effortlessly. I won’t bore you overmuch with details but it’s easy enough to do by casting your Stringbuffer to a char[] array and then walking the array as necessary and calculating the outcome as you go along. This method with its helpers should allow you to process most mathematically expressed Strings. Obviously if you’re working with floats etc, you would have to overload and extend the metaphor, but generally what’s needed is not much beyond the skeletal framework we have here (you’ll need to handle decimal points being the main difference). Typically your method for the LovecraftEvaluator class should look something like this


public int sbMathEvaluator(StringBuffer sb)
    {
    int result = 0;
    char operator = ' ';
    String tmp = "";

    char[] aEval = sb.toString().toCharArray();
    int last = aEval.length;

    for (int i = 0; i < aEval.length; i++) // each character we have....
    {
    if(!isInt(aEval[i])) // if it's not an int we'll change the value of the operator and go back to the top of the loop
    {
    operator = aEval[i];
    tmp = ""; // reset tmp
    continue;
    }

// if we got here it's an int (at least 1) & there may be more so don't calc just yet....
    tmp += Character.toString(aEval[i]);

    if(i + 1 == last){ // we really don't want an array exception here so this is the final calc on the last number
    result = calc(result,operator,toInt(tmp))    ;
        break;}
    if(!isInt(aEval[i + 1])) // if the next char's an operator, recalculate
    {
    result = calc(result,operator,toInt(tmp))    ;
    }

    }

    return result;
    }

The helpers you will need are as follows. Firstly the calc method which takes the running total, the operation to perform, and the value to apply to it as arguments. Although we’re just doing addition it doesn’t hurt to make it relatively generic. You could add other operators etc, but let’s keep it relatively simple for illustrative purposes…


public int calc(int result, char operator, int val)
    {
    if (operator == '+')
    {return result + val;}
    if (operator == '-')
    {return result - val;}
    if (operator == '*')
    {return result * val;}
    if (operator == '/')
    {return result / val;}
// no op possible? just throw back the result unmutated
    return result;
    }

And we’ll need a few little syntax helpers. Feel free to rip these and rep them as necessary, they’re for convenience mainly.


   public int toInt(String tmp)
    {
    return Integer.parseInt(tmp);
    }

    public int toInt(char c)
    {
    return Character.getNumericValue(c);
    }

    public boolean isInt(char c)
    {
    int i = Character.getNumericValue(c);
    if (i >= 0){        return true;}
    return false;
    }

public int[] toIntArray(List<Integer> integerList) {
int[] intArray = new int[integerList.size()];
for (int i = 0; i < integerList.size(); i++) {
intArray[i] = integerList.get(i);
}
return intArray;
}

As an afterthought, if you're going to reduce something of the length of teh Necronomicon to its numerical value, you'd probably be advised to add the earlier countStringContents method to the class also and set the capacity of your target replacement StringBuffer to twice that number + a few e.g.

String test = "The complete and unexpurgated contents of the Necronomicon.... etc";
LovecraftEvaluator le = new LovecraftEvaluator();
String vowels = "aeiouAEIOU";
int[] positions = le.locateStringContents(new StringBuffer(test), new StringBuffer(vowels));
int numVowels = le.countStringContents(test,vowels);
numvowels = (numVowels * 2) + 50;

int ctr = 0;
StringBuffer sOut = new StringBuffer();
sOut.ensureCapacity(numVowels);
for (int i = 0; i < positions.length; i++)
{
sOut.append("+1");
}
sOut.append(0);