I have a input like
string input = \"14 + 2 * 32 / 60 + 43 - 7 + 3 - 1 + 0 * 7 + 87 - 32 / 34\";
// up to 10MB string size
int result = Calc(input); // 11
Per comments, this answer does not give a performant solution. I'll leave it here as there are points to be considered / which may be of interest to others finding this thread in future; but as people have said below, this is far from the most performant solution.
The .net framework already supplies a way to handle formulas given as strings:
var formula = "14 + 2 * 32 / 60 + 43 - 7 + 3 - 1 + 0 * 7 + 87 - 32 / 34";
var result = new DataTable().Compute(formula, null);
Console.WriteLine(result); //returns 139.125490196078
Per the comments thread I need to point out some things:
No; this follows the normal rules of maths.
I assume that your amended rules are to simplify writing code to handle them, rather than because you want to support a new branch of mathematics? If that's the case, I'd argue against that. People will expect things to behave in a certain way; so you'd have to ensure that anyone sending equations to your code was primed with the knowledge to expect the rules of this new-maths rather than being able to use their existing expectations.
There isn't an option to change the rules here; so if your requirement is to change the rules of maths, this won't work for you.
No. However it should perform well given MS spend a lot of time optimising their code, and so will likely perform faster than any hand-rolled code to do the same (though admittedly this code does a lot more than just support the four main operators; so it's not doing exactly the same).
Per MatthewWatson's specific comment (i.e. calling the DataTable constructor incurs a significant overhead) you'd want to create and then re-use one instance of this object. Depending on what your solution looks like there are various ways to do that; here's one:
interface ICalculator //if we use an interface we can easily switch from datatable to some other calulator; useful for testing, or if we wanted to compare different calculators without much recoding
{
T Calculate(string expression) where T: struct;
}
class DataTableCalculator: ICalculator
{
readonly DataTable dataTable = new DataTable();
public DataTableCalculator(){}
public T Calculate(string expression) where T: struct =>
(T)dataTable.Compute(expression, null);
}
class Calculator: ICalculator
{
static ICalculator internalInstance;
public Calculator(){}
public void InitialiseCalculator (ICalculator calculator)
{
if (internalInstance != null)
{
throw new InvalidOperationException("Calculator has already been initialised");
}
internalInstance = calculator;
}
public T Calculate(string expression) where T: struct =>
internalInstance.Calculate(expression);
}
//then we use it on our code
void Main()
{
var calculator1 = new Calculator();
calculator1.InitialiseCalculator(new DataTableCalculator());
var equation = "14 + 2 * 32 / 60 + 43 - 7 + 3 - 1 + 0 * 7 + 87 - 32 / 34";
Console.WriteLine(calculator1.Calculate(equation)); //139.125490196078
equation = "1 + 2 - 3 + 4";
Console.WriteLine(calculator1.Calculate(equation)); //4
calculator1 = null;
System.GC.Collect(); //in reality we'd pretty much never do this, but just to illustrate that our static variable continues fro the life of the app domain rather than the life of the instance
var calculator2 = new Calculator();
//calculator2.InitialiseCalculator(new DataTableCalculator()); //uncomment this and you'll get an error; i.e. the calulator should only be initialised once.
equation = "1 + 2 - 3 + 4 / 5 * 6 - 7 / 8 + 9";
Console.WriteLine(calculator2.Calculate(equation)); //12.925
}
NB: The above solution uses a static variable; some people are against use of statics. For this scenario (i.e. where during the lifetime of the application you're only going to require one way of doing calculations) this is a legitimate use case. If you wanted to support switching the calculator at runtime a different approach would be required.
Having run some performance tests:
DataTable.Compute
method's biggest problem is that for equations the size of which you're dealing with it throws a StackOverflowException
; (i.e. based on your equation generator's loop for (int i = 0; i < 1000000; i++)
.i < 1000
), the compute method (including constructor and Convert.ToInt32
on the double
result) takes almost 100 times longer.Link to the docs: https://msdn.microsoft.com/en-us/library/system.data.datatable.compute%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396