Convert string into mathematical expression [duplicate] - c#

This question already has answers here:
Is there a string math evaluator in .NET?
(18 answers)
Closed 3 years ago.
Is there an easy way to parse a simple math expression represented as a string such as (x+(2*x)/(1-x)), provide a value for x, and get a result?
I looked at the VSAEngine per several online examples, however, I am getting a warning that this assembly has been deprecated and not to use it.
If it makes any differences, I am using .NET 4.0.

I urge caution against choosing an existing generic expression evaluator over a purpose-built math evaluator. The reason for this is expression evaluators are not limited to math. A clever individual could use this to create an instance of any type in the framework and call any method on the type, and that would allow him to do some decidedly unwelcome things. For example: new System.Net.WebClient().DownloadFile("illegalchildpornurl", "C:\openme.gif") will evaluate just fine in most of those, and do just what it sounds like it would (and make you a felon at the same time).
This doesn't mean don't look for something that's already written; it just means be careful. You want one that does math, and only math. Most of what's already out there isn't that picky.

I was recently using mXparser, which is a math parser library. It gives you a lot of flexibility, such as variables, functions, constants, operators. You will find below a few usage examples:
Example 1 - simple formula
Expression e = new Expression("1 + pi");
double v = e.calculate();
Example 2 - formula with variables, functions, etc.
Argument x = new Argument("x = 2");
Constant a = new Constant("a = sin(10)");
Function f = new Function("f(t) = t^2");
Expression e = new Expression("2*x + a - f(10)", x, a, f);
double v = e.calculate();
https://mxparser.codeplex.com/
https://mathparser.org/
Found recntly - you can try the syntax (and see the use case) via the Scalar Calculator app that is powered by mXparser.
Best regards

You can try using DataTable.Compute.
A related one is DataColumn.Expression.
Also check out: Doing math in vb.net like Eval in javascript
Note: I haven't used these myself.

I would also have a look at Jace (https://github.com/pieterderycke/Jace). Jace is a high performance math parser and calculation engine that supports all the .NET flavors (.NET 4.x, Windows Phone, Windows Store, ...). Jace is also available through NuGet: https://www.nuget.org/packages/Jace

Here is one way to do it. This code is written in Java. Note it does not handle negative numbers right now, but you can add that.
public class ExpressionParser {
public double eval(String exp, Map<String, Double> vars){
int bracketCounter = 0;
int operatorIndex = -1;
for(int i=0; i<exp.length(); i++){
char c = exp.charAt(i);
if(c == '(') bracketCounter++;
else if(c == ')') bracketCounter--;
else if((c == '+' || c == '-') && bracketCounter == 0){
operatorIndex = i;
break;
}
else if((c == '*' || c == '/') && bracketCounter == 0 && operatorIndex < 0){
operatorIndex = i;
}
}
if(operatorIndex < 0){
exp = exp.trim();
if(exp.charAt(0) == '(' && exp.charAt(exp.length()-1) == ')')
return eval(exp.substring(1, exp.length()-1), vars);
else if(vars.containsKey(exp))
return vars.get(exp);
else
return Double.parseDouble(exp);
}
else{
switch(exp.charAt(operatorIndex)){
case '+':
return eval(exp.substring(0, operatorIndex), vars) + eval(exp.substring(operatorIndex+1), vars);
case '-':
return eval(exp.substring(0, operatorIndex), vars) - eval(exp.substring(operatorIndex+1), vars);
case '*':
return eval(exp.substring(0, operatorIndex), vars) * eval(exp.substring(operatorIndex+1), vars);
case '/':
return eval(exp.substring(0, operatorIndex), vars) / eval(exp.substring(operatorIndex+1), vars);
}
}
return 0;
}
}
You need to import java.util.Map.
Here is how I use this code:
ExpressionParser p = new ExpressionParser();
Map vars = new HashMap<String, Double>();
vars.put("x", 2.50);
System.out.println(p.eval(" 5 + 6 * x - 1", vars));

Another option you may want to look into is the Spring.NET Framework's expression evaluation functionality. It can do a lot more than math, too.
However, the entire Spring.NET Framework might be a bit of overkill for your needs if you don't require the rest of the functionality.

Related: Equation expression parser with precedence.

As I answered in this thread (Best Free C# Math Parser using variables, user defined functions, custom operators), you can use Mathos Parser, which you can simply paste into your source code.
Mathos.Parser.MathParser parser = new Mathos.Parser.MathParser();
string expr = "(x+(2*x)/(1-x))"; // the expression
decimal result = 0; // the storage of the result
parser.LocalVariables.Add("x", 41); // 41 is the value of x
result = parser.Parse(expr); // parsing
Console.WriteLine(result); // 38.95

I recomend you to use MEEL for this.
// parse string to IExpression (symbolic type)
IExpression expression = BaseExpression.Parse("(x+(2*x)/(1-x))");
// create your own collection for attributes
var attributes = new MathAttributeCollection();
// create local variable named "x" with value 5
var attributeX = new ScalarAttrInt("x") {Value = new ScalarConstInt(5)};
attributes.Add(attributeX);
// execute math expression where x=5
var result = expression.Execute(attributes);
MessageBox.Show(result.GetText());
// result: 2.5

Related

Calculator using all the input data on a single line

(later edit)
Just finished this assignment. I was limited to using System and using System.collections.Generic. Nothing else. I ended up using a List<> from the from the collections.Generic. and the principles of Polish Notation. After every "Encounter" of a operation sign (+-/*) I used the index - 1 and - 2 to get the previous 2 numbers and make the operation. Then I used .Insert to insert the result at the current index, afterwards I used .Remove to substract from the list the numbers and the operator which I just used and after I continued with the recursive function on the new list obtained starting with the index from 0 again. The polish notation articles posted in the comments helped me the most to understand the logic behind this
*
I'm trying to figure out from the past several days a way to implement the following logic into a c# program. Using only System.
This is a small console calculator where the input is inserted on a single line in the console.
For example the following input + / * + 65 32 46 2 - 1 1.25 should be translated into a math operation looking like this => ((65 + 32) * 46) / 2 + (1 - 1.25)
Another example would be * + 3 2 - 9.5 6.5: this should be calculated in the following order 3 + 2 * (9.5 - 6.5).
Another example / + 5 3 2 equals with => (5 + 3) / 2
I have to make the function recursive.
I figured out how to make it if all the operations sings are in front of the digits in the input. (I just separate the operator list and inverse it and I get two separate lists: one containing the operation signs and the other containing the numbers). What I'm struggling is to figure out a way to do the operations if there is a math sign in between the numbers (like in the first an the second example).
I don't necessarily need a code for this, maybe an explanation or if somebody could point me to the right direction where I can read about some algorithm / math formula or something that could help me better understand how to implement this.
Thank you in advance.
The normal method of evaluating Polish Notation expressions doesn't require recursion, you use a stack (like Forth or RPN) and evaluate as you go.
An easy way to create a recursive version is to consider the expression language BNF then crafting a recursive descent parser from the grammer.
For example, a possible BNF would be:
expr = op arg arg
op = [+-*/] // cheating; use regex to describe terminal
arg = number | expr
number = [0-9]+ // using Regex to describe terminal
So now you would create methods for each element:
double expr() {
string opStr = op();
double arg1 = arg();
double arg2 = arg();
double ans;
switch (opStr) {
case "+":
ans = arg1+arg2;
break;
// case and so on
}
return ans;
}
static string operators = "+-*/";
string op() {
if (operators.Contains(peekChar()))
return nextCharAsStr();
else
throw new Exception("Missing operator");
}
double arg() {
double? num = number();
if (num.HasValue)
return num.Value;
else
return expr();
}
double? number() {
string ans = "";
while (Char.IsDigit(peekChar()))
ans += nextCharAsStr();
if (String.IsNullOrEmpty(ans))
return null;
else
return Double.Parse(ans);
}
NOTE: Whitespace and end of string is left as an exercise to the reader.
You could also use a tokenizer that extracts terminals from the string instead of working directly with characters in the parser terminal methods.
There are different kinds of recursion. The most common (code recursion) is probably what you're asking about, wherein a function (or set of functions) call each other until some sort of exit condition is reached.
For this, I'd go with more of a data recursive approach. This version only supports single-digit operands and binary operators.
(Really bad pseudocode below).
stack<char> operators;
stack<char> operands;
for(var i=input.Length-1; i>=0; --i)
{
var c = input[i];
if (Char.IsDigit(c)) /// note that this only handles single-digit numbers.
operands.push(c);
else
operators.push(c);
if (operators.count >= 1 && operands.count >= 2)
{
var operator = operators.pop();
/// handle binary operators
left = operands.pop();
right = operands.pop();
switch(operator) {
case '+' : result = left + right; break;
case '-' : result = left - right; break;
case '*' : result = left * right; break;
case '/' : result = left / right; break;
}
operands.push(result);
}
}
var result = operands.pop();
When that completes, your operands stack should have only one item which is the result of the expression. and operators should be empty.
If you have leftover operators, then there weren't enough values in the input. If you have more than one value in operands, there weren't enough operators in the input. If you have zero operands (ie, no result), then there weren't any in the input to start with.
For a real implementation, you'd want to parse the string to get multi-digit operands, handle unary operators, ignore whitespace, etc.
Edit: reversed the loop direction.
So you want something like this:
static void Main(string[] args)
{
Expression expr = Parse("+ / * + 65 32 46 2 - 1 1.25");
Console.WriteLine($"f = {expr}");
// f = ((((65 + 32) * 46) / 2) + (1 - 1.25))
Func<double> f = Compile(expr);
Console.WriteLine($"f = {f()}");
// f = 2230.75
}
public static Func<double> Compile(Expression expr)
{
return Expression.Lambda(expr).Compile() as Func<double>;
}
public static Expression Parse(string input)
{
var tokens = new Tokenizer(input);
return Parse(tokens);
}
There are two parts this.
First split the input into substrings using the white space. Use input.Split() for that. Then, scan the parts from right to left (from last to first) and extract tokens. A token is a substring that might represent a number or an operation (or a variable if you extend the code below). This is the job of Tokenizer class, the implements IEnumerable<TokenInfo> to act as a collection. A TokenInfo is a data type that describes each token, and it contains the following:
An identifying tag of enum Token.Number, Token.Operator or Token.End
The string text identifying the operator (when applicable)
The double value representing a number (when applicable)
public enum Token
{
Unknown,
Number,
Operator,
End
}
public struct TokenInfo
{
public TokenInfo(Token token)
{
Token=token;
Text = string.Empty;
Value = 0;
}
public TokenInfo(string op) : this(Token.Operator)
{
Text = op;
}
public TokenInfo(double value) : this(Token.Number)
{
Value = value;
}
public TokenInfo(Token token, string text) : this(token)
{
Text = text;
}
public Token Token { get; }
public string Text { get; }
public double Value { get; }
}
public class Tokenizer : IEnumerable<TokenInfo>
{
readonly string[] operators = new[] { "+", "-", "*", "/" };
readonly List<TokenInfo> tokens;
public Tokenizer(string input)
{
tokens = new List<TokenInfo>();
string[] parts = input.Split(' ');
for (int i = parts.Length - 1; i >= 0; i--)
{
if (operators.Contains(parts[i]))
{
tokens.Add(new TokenInfo(parts[i]));
}
else if (double.TryParse(parts[i], out var x))
{
tokens.Add(new TokenInfo(x));
}
else
{
tokens.Add(new TokenInfo(Token.Unknown, parts[i]));
}
}
tokens.Add(new TokenInfo(Token.End));
}
public IEnumerator<TokenInfo> GetEnumerator()
{
return tokens.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Parse the list of tokens and built the expression tree. Keep a Stack<Expression> and assume the top expression is always the result of the previous operation. Also what gets returned in the end is the top of the stack, if it is only one expression tall. Otherwise, there is an imbalance in the operators and/or the values.
Do the following for each Token type
Token.Number parse the value of the number using double.TryParse() and add a ConstantExpression on the stack
Token.Operator pull two values from the top of the stack and add an appropriate BinaryEpression to the stack. Possible options are Expression.Add(), Expression.Subtract(), Expression.Multiply(), and Expression.Divide()
Token.End return the last expression of the top of the stack.
public static Expression Parse(IEnumerable<TokenInfo> tokens)
{
Stack<Expression> stack = new Stack<Expression>();
foreach (var item in tokens)
{
switch (item.Token)
{
case Token.Number:
{
var expr = Expression.Constant(item.Value, typeof(double));
stack.Push(expr);
}
break;
case Token.Operator:
{
var arg1 = stack.Pop();
var arg2 = stack.Pop();
switch (item.Text)
{
case "+":
stack.Push(Expression.Add(arg1, arg2));
break;
case "-":
stack.Push(Expression.Subtract(arg1, arg2));
break;
case "*":
stack.Push(Expression.Multiply(arg1, arg2));
break;
case "/":
stack.Push(Expression.Divide(arg1, arg2));
break;
default:
throw new NotSupportedException($"Invalid operator {item.Text}.");
}
}
break;
case Token.End:
{
if (stack.Count != 1)
{
throw new InvalidOperationException("Imbalanced expression tree.");
}
return stack.Pop();
}
}
}
throw new ArgumentException($"Invalid expression.", nameof(tokens));
}
Optionally the Expression can be compiled into a function, using the Compile() function above. This code assumes there are no ParameterExpression nodes in the expression tree which would need to be accounted for as arguments to the compiled function.
The addition of variables in the expression and the subsequent compilation into unary or binary functions is pretty straightforward. Add a Token for a variable, produce an appropriate TokenInfo when a string that is not an operator is encountered in the tokenizer and then add ParameterExpression objects to the stack, just like numeric values are added. The compilation parts need references to these parameters from the tree so a custom ExpressionVistor would be needed to extract those nodes.

C# Can a comparison in a String value return a Boolean value. eg. "5 < 10" return true

Is there a way a comparison in a string value can return a Boolean value. Example.
If (5 > 5000) would obviously return a false value. But what i wanted to do is have the "5 > 5000" return a false value.
Example.
string com = "5 > 10";
so is there a way to make this com variable return a false value as if it was a comparison between integers.
No built-in way but NCalc can help here
NCalc.Expression expr = new NCalc.Expression("5>10");
bool b = (bool)expr.Evaluate();
You can even use parameters
NCalc.Expression expr = new NCalc.Expression("a<b");
expr.EvaluateParameter += (name, args) =>
{
if (name == "a") args.Result = 5;
if (name == "b") args.Result = 10;
};
bool b = (bool)expr.Evaluate();
There is no built-in way to do this.
Although there are a couple of ways to approach this, one is to simply parse the text yourself. I did this in the code presented in the article A C# Expression Evaluator. You might want to review that code.
No, this can't be done directly.
You should write your own class or extend the String class. For handling a string such as "5 < 10", you need your own method.
You should search the string for signs that indicate comparison, such as "<", "==" etc, then split it and perform the comparison.
Basically: doing it yourself is the only way, but you can try to do it in an elegant way.
Short answer: no.
Long answer: feel free to parse the string yourself, looking for > < and =. Split by whitespace, parse ints then evaluate. It might get harder if you want it to work with parentheses as well...
Not directly, per se (short of the unsafe Javascript eval-execute-my-data hack) but you can try parsing it yourself, depending on how complicated of an expression you want to accept. For example, this should work with the string you have:
var arr = com.Split('>').Select(x=>int.Parse(x.Trim())).ToArray();
return arr[0] > arr[1];
You can also use regular expressions to get more complicated (untested, but it ought to work):
var r = new Regex(#"(\d+)\b*(<|>|=|<=|>=)\b*(\d+)")
var match = r.Match(com);
if(match.Success)
{
var a = int.Parse(match.Captures[0]);
var b = int.Parse(match.Captures[2]);
switch(match.Captures[1])
{
case "<":
return a < b;
case "=":
return a = b;
case ">":
return a > b;
case "<=":
return a <= b;
case "<=":
return a >= b;
}
}
//uh-oh
throw new ArgumentException("com");
Consider using FLEE:
Flee is an expression parser and evaluator for the .NET framework. It allows you to compute the value of string expressions such as sqrt(a^2 + b^2) at runtime. It uses a custom compiler, strongly-typed expression language, and lightweight codegen to compile expressions directly to IL. This means that expression evaluation is extremely fast and efficient. Try out the demo, which lets you generate images based on expressions, and see for yourself.
With FLEE you can easily accomplish this using something like:
var con = new ExpressionContext();
const string com = #"5 > 5000";
var comparison = con.CompileDynamic(com);
var result = comparison.Evaluate();
MessageBox.Show(result.ToString());
HTH...

Parsing Conditional Expressions to String

I'm looking for a way of parsing a conditional expression to a string.
The best example I can think of is LINQ-to-SQL. It uses ExpressionVisitors to format "Where" clauses. Example:
from a in b where a.x == 5 && a.y < 3 select a
That would translate into the following string (approximately, MSSQL is not current for me):
"SELECT * FROM b WHERE x = 5 AND y < 3"
From what I've read, this was done using the ExpressionVisitor class, as explained in this article: Link
Now the problem is that I don't use LINQ, but I need this particular functionality. Is there a way of parsing a condition like that? I'm willing to do anything with reflection, delegates, lambda, etc.
Honestly, I don't think it's possible, but my brain is a bit fried (read: be nice if the question is ridiculous), so I figured I might just as well give S/O a try.
EDIT: Final usage example:
// Usage:
foo.Bar(foo => foo.X == 5 && foo.Y < 3)
// Ideal string output (variable name (foo) is not needed):
"foo.X == 5 && foo.Y < 3"
EDIT 2: Yes, a number can be lower than 3 and equal to 5. Told you my brain is fried.
If it is about about building the expression tree itself, then you could leverage C# compiler abilities.
It is legal to pass a lambda expression to a function acception an Expression>, as long as type arguments of Func are known. For example
private static void PrintExpression(Expression<Func<int, bool>> lambda)
{
Console.WriteLine(lambda.ToString());
}
can be called as
PrintExpression(a=> a > 0 && a < 5);
You can improvise with generics as
private static void PrintExpression<T1,T2>(Expression<Func<T1, T2>> lambda)
{
Console.WriteLine(lambda.ToString());
}
and calling it with
PrintExpression<int, bool>(a=> a > 0 && a < 5);
For custom printing of the expression part, you can write a simple recursive function that prints an expression or any other logic that suits you.
Remember, the lambda expression is compiled into an Expression at compile time - so can't subsititute it with already compiled Func.
As an alternative to this, you can always build a custom query provider, but that would be slightly deviating from the purpose - as you'd need to bind it some sort of queryable (custom again).
Try something like this:
static string GetExpressionString<T>(Expression<Func<T, bool>> expression)
{
return expression.Body.ToString();
}
Usage as so:
string s = GetExpressionString<Foo>(foo => foo.X == 5 && foo.Y < 3);
Which will return:
((foo.X = 5) && (foo.Y < 3))

Best solution for an StringToInt function in C#

I were asked to do an StringToInt / Int.parse function on the white board in an job interview last week and did not perform very good but I came up with some sort of solution. Later when back home I made one in Visual Studion and I wonder if there are any better solution than mine below.
Have not bothred with any more error handling except checking that the string only contains digits.
private int StrToInt(string tmpString)
{
int tmpResult = 0;
System.Text.Encoding ascii = System.Text.Encoding.ASCII;
byte[] tmpByte = ascii.GetBytes(tmpString);
for (int i = 0; i <= tmpString.Length-1; i++)
{
// Check whatever the Character is an valid digit
if (tmpByte[i] > 47 && tmpByte[i] <= 58)
// Here I'm using the lenght-1 of the string to set the power and multiply this to the value
tmpResult += (tmpByte[i] - 48) * ((int)Math.Pow(10, (tmpString.Length-i)-1));
else
throw new Exception("Non valid character in string");
}
return tmpResult;
}
I'll take a contrarian approach.
public int? ToInt(this string mightBeInt)
{
int convertedInt;
if (int.TryParse(mightBeInt, out convertedInt))
{
return convertedInt;
}
return null;
}
After being told that this wasn't the point of the question, I'd argue that the question tests C coding skills, not C#. I'd further argue that treating strings as arrays of characters is a very bad habit in .NET, because strings are unicode, and in any application that might be globalized, making any assumption at all about character representations will get you in trouble, sooner or later. Further, the framework already provides a conversion method, and it will be more efficient and reliable than anything a developer would toss off in such a hurry. It's always a bad idea to re-invent framework functionality.
Then I would point out that by writing an extension method, I've created a very useful extension to the string class, something that I would actually use in production code.
If that argument loses me the job, I probably wouldn't want to work there anyway.
EDIT: As a couple of people have pointed out, I missed the "out" keyword in TryParse. Fixed.
Converting to a byte array is unnecessary, because a string is already an array of chars. Also, magic numbers such as 48 should be avoided in favor of readable constants such as '0'. Here's how I'd do it:
int result = 0;
for (int i = str.Length - 1, factor = 1; i >= 0; i--, factor *= 10)
result += (str[i] - '0') * factor;
For each character (starting from the end), add its numeric value times the correct power of 10 to the result. The power of 10 is calculated by multiplying it with 10 repeatedly, instead of unnecessarily using Math.Pow.
I think your solution is reasonably ok, but instead of doing math.pow, I would do:
tmpResult = 10 * tmpResult + (tmpByte[i] - 48);
Also, check the length against the length of tmpByte rather than tmpString. Not that it normally should matter, but it is rather odd to loop over one array while checking the length of another.
And, you could replace the for loop with a foreach statement.
If you want a simple non-framework using implementation, how 'bout this:
"1234".Aggregate(0, (s,c)=> c-'0'+10*s)
...and a note that you'd better be sure that the string consists solely of decimal digits before using this method.
Alternately, use an int? as the aggregate value to deal with error handling:
"12x34".Aggregate((int?)0, (s,c)=> c>='0'&&c<='9' ? c-'0'+10*s : null)
...this time with the note that empty strings evaluate to 0, which may not be most appropriate behavior - and no range checking or negative numbers are supported; both of which aren't hard to add but require unpretty looking wordy code :-).
Obviously, in practice you'd just use the built-in parsing methods. I actually use the following extension method and a bunch of nearly identical siblings in real projects:
public static int? ParseAsInt32(this string s, NumberStyles style, IFormatProvider provider) {
int val;
if (int.TryParse(s, style, provider, out val)) return val;
else return null;
}
Though this could be expressed slightly shorter using the ternary ? : operator doing so would mean relying on side-effects within an expression, which isn't a boon to readability in my experience.
Just because i like Linq:
string t = "1234";
var result = t.Select((c, i) => (c - '0') * Math.Pow(10, t.Length - i - 1)).Sum();
I agree with Cyclon Cat, they probably want someone who will utilize existing functionality.
But I would write the method a little bit different.
public int? ToInt(this string mightBeInt)
{
int number = 0;
if (Int32.TryParse(mightBeInt, out number))
return number;
return null;
}
Int32.TryParse does not allow properties to be given as out parameter.
I was asked this question over 9000 times on interviews :) This version is capable of handling negative numbers and handles other conditions very well:
public static int ToInt(string s)
{
bool isNegative = false, gotAnyDigit = false;
int result = 0;
foreach (var ch in s ?? "")
{
if(ch == '-' && !(gotAnyDigit || isNegative))
{
isNegative = true;
}
else if(char.IsDigit(ch))
{
result = result*10 + (ch - '0');
gotAnyDigit = true;
}
else
{
throw new ArgumentException("Not a number");
}
}
if (!gotAnyDigit)
throw new ArgumentException("Not a number");
return isNegative ? -result : result;
}
and a couple of lazy tests:
[TestFixture]
public class Tests
{
[Test]
public void CommonCases()
{
foreach (var sample in new[]
{
new {e = 123, s = "123"},
new {e = 110, s = "000110"},
new {e = -011000, s = "-011000"},
new {e = 0, s = "0"},
new {e = 1, s = "1"},
new {e = -2, s = "-2"},
new {e = -12223, s = "-12223"},
new {e = int.MaxValue, s = int.MaxValue.ToString()},
new {e = int.MinValue, s = int.MinValue.ToString()}
})
{
Assert.AreEqual(sample.e, Impl.ToInt(sample.s));
}
}
[Test]
public void BadCases()
{
var samples = new[] { "1231a", null, "", "a", "-a", "-", "12-23", "--1" };
var errCount = 0;
foreach (var sample in samples)
{
try
{
Impl.ToInt(sample);
}
catch(ArgumentException)
{
errCount++;
}
}
Assert.AreEqual(samples.Length, errCount);
}
}

How to force c# binary int division to return a double?

How to force double x = 3 / 2; to return 1.5 in x without the D suffix or casting? Is there any kind of operator overload that can be done? Or some compiler option?
Amazingly, it's not so simple to add the casting or suffix for the following reason:
Business users need to write and debug their own formulas. Presently C# is getting used like a DSL (domain specific language) in that these users aren't computer science engineers. So all they know is how to edit and create a few types of classes to hold their "business rules" which are generally just math formulas.
But they always assume that double x = 3 / 2; will return x = 1.5
however in C# that returns 1.
A. they always forget this, waste time debugging, call me for support and we fix it.
B. they think it's very ugly and hurts the readability of their business rules.
As you know, DSL's need to be more like natural language.
Yes. We are planning to move to Boo and build a DSL based on it but that's down the road.
Is there a simple solution to make double x = 3 / 2; return 1.5 by something external to the class so it's invisible to the users?
Thanks!
Wayne
No, there's no solution that can make 3 / 2 return 1.5.
The only workaround taking into consideration your constraints is to discourage the users to use literals in the formula. Encourage them to use constants. Or, if they really need to use literals, Encourage them to use literals with a decimal point.
never say never...
The (double)3/2 solution looks nice...
but it failed for 4+5/6
try this:
donated to the public domain to be used freely by SymbolicComputation.com.
It's alpha but you can try it out, I've only run it on a few tests, my site and software should be up soon.
It uses Microsoft's Roslyn, it'll put a 'd' after every number if all goes well. Roslyn is alpha too, but it will parse a fair bit of C#.
public static String AddDSuffixesToEquation(String inEquation)
{
SyntaxNode syntaxNode = EquationToSyntaxNode(inEquation);
List<SyntaxNode> branches = syntaxNode.DescendentNodesAndSelf().ToList();
List<Int32> numericBranchIndexes = new List<int>();
List<SyntaxNode> replacements = new List<SyntaxNode>();
SyntaxNode replacement;
String lStr;
Int32 L;
for (L = 0; L < branches.Count; L++)
{
if (branches[L].Kind == SyntaxKind.NumericLiteralExpression)
{
numericBranchIndexes.Add(L);
lStr = branches[L].ToString() + "d";
replacement = EquationToSyntaxNode(lStr);
replacements.Add(replacement);
}
}
replacement = EquationToSyntaxNode(inEquation);
List<SyntaxNode> replaceMeBranches;
for (L = numericBranchIndexes.Count - 1; L >= 0; L--)
{
replaceMeBranches = replacement.DescendentNodesAndSelf().ToList();
replacement = replacement.ReplaceNode(replaceMeBranches[numericBranchIndexes[L]],replacements[L]);
}
return replacement.ToString();
}
public static SyntaxNode EquationToSyntaxNode(String inEquation)
{
SyntaxTree tree = EquationToSyntaxTree(inEquation);
return EquationSyntaxTreeToEquationSyntaxNode(tree);
}
public static SyntaxTree EquationToSyntaxTree(String inEquation)
{
return SyntaxTree.ParseCompilationUnit("using System; class Calc { public static object Eval() { return " + inEquation + "; } }");
}
public static SyntaxNode EquationSyntaxTreeToEquationSyntaxNode(SyntaxTree syntaxTree)
{
SyntaxNode syntaxNode = syntaxTree.Root.DescendentNodes().First(x => x.Kind == SyntaxKind.ReturnStatement);
return syntaxNode.ChildNodes().First();
}
simple, if I'm not mistaken:
double x = 3D / 2D;
One solution would be writing a method that does this for them and teach them to use it. Your method would always take in doubles and the answer will always have the correct number of decimals.
I'm not pretty sure, but I believe you can get a double using 3.0/2.0
But if you think .0 just as another way of suffixing then it's not the answer too :-)
Maybe you can try RPN Expression Parser Class for now or bcParser? These are very small expression parsing libraries.
I like strong, statically typed languages for my own work, but I don't think they're suited for beginners who have no interest in becoming professionals.
So I'd have to say unfortunately your choice of C# might not of been the best for that audience.
Boo seems to be statically typed to. Have you thought about embedding a Javascript engine, Python, or some other dynamically typed engine? These usually are not that hard to plug into an existing application and you have the benefit of lots of existing documentation.
Perhaps an extenstion method on int32?
Preprocess formulas before passing them to the c# compiler. Do something like:
formula = Regex.Replace(formula, #"(^|[\^\s\+\*\/-])(\d+)(?![DF\.])", "$1$2D")
To convert integer literals to double literals.
Alternately, you could use a simple state machine to track whether or not you're in a string literal or comment rather than blindly replacing, but for simple formulas I think a regex will suffice.
Try doing it like this:
double result = (double) 3 / 2;
result = 1.5

Categories