- Home /
Recovering function from input string
I am looking for a way how to allow user to input function and get it's value at certain points. Lets say I have:
public string independentArgument = "x";
public string myFunction = "x*x";
public float x = 3.1f;
public float y;
void Start(){
y = GetValue( independentArgument, myFunction, x );
print(y);
}
float GetValue(string s1, string s2, float f1){
float f2;
???
return f2
}
The question here is how to "search" myFunction string to build up expression for y calculation?
Answer by Bunny83 · Nov 19, 2014 at 04:52 PM
I've written a very simple expression parser some time ago. I just posted it on the wiki (ExpressionParser). It also allows you to specify custom functions. It works entirely on double values. It supports most functions from the System.Math class.
Simple example of how to add a custom method:
var parser = new ExpressionParser();
// add fibonacci function. Takes zero based index
parser.AddFunc("fib", (p) =>{
double V1 = 1;
double V2 = 0;
int count = (int)p.FirstOrDefault();
for(int i = 0; i < count; i++)
{
double V = V1 + V2;
V2 = V1;
V1 = V;
}
return V1;
});
var f = parser.EvaluateExpression("fib(x)").ToDelegate("x");
for (int i = 0; i < 20; i++ )
Debug.Log("("+i+")" + f(i));
edit
Since i haven't tested the parser in Unity it had some errors due to .NET / Mono incompatibilities. I've fixed those (small) errors and created an example:
ExpressionParserTest(requires webplayer)
I've added links to the parser and the script for this demo below the webplayer. The scene just contains a camera with the ExpressionParserTest.cs attached.
Some notes on the demo:
all unknown parameters are simply fed with the same "x"-value
the x value goes from -100 to +100 with a step size of 0.1 So it calculates 2000 values each time it's drawn.
The time "constant" is simple Unity's Time.time.
I've added a "repeat" and "pingpong" function which works the same as Unity's
The visible screen area goes from -100 to 100 in x and -50 to 50 in y
When I save the class from wiki to ExpressionParser.cs file I getting two errors appearing:
Assets/scripts/ExpressionParser.cs(73,34): error CS1502: The best overloaded method match for `string.Join(string, string[])' has some invalid arguments
and the second one:
Assets/scripts/ExpressionParser.cs(73,34): error CS1503: Argument `#2' cannot convert `System.Collections.Generic.IEnumerable<string>' expression to type `string[]'
Any idea how to fix them?
Yes, sure... It looks like $$anonymous$$ono doesn't have the string.Join overload that takes an IEnumerable... I'll add a .ToArray ^^
I've written and tested the class in Visual Studio. Since it just uses standard classes i would have expected it to work out of the box...
What do you mean by "add the whole function together"? I don't get that...
edit
@chanfort:
Ok, i've replaced everything that caused problems (was more than i expected...)
@chanfort: Just in case you haven't seen the edit, I've added an example ;)
Wonderfull, it works like a charm now!
Oh, forget about previous comment - I was just thinking that it was like a method, but it's a set of classes.
By the way, you can also try to build a simple usage "helper" class, which would take just strings as input, uses all namespaces, etc inside it and returns to end user double or float :)
But in general it's very great job, as it calculates all these $$anonymous$$athf functions and allows users to enter their equations
We can finally close this question.
@chanfort: Hmm, i'm not sure what kind of helper you have in $$anonymous$$d. When you have created a parser object for parameter less expressions you can use
double val = parser.Evaluate("5+3^4-5/7");
When you have parameters you either have to create a delegate or use the expression that is returned by EvaluateExpression:
ExpressionDelegate func = parser.EvaluateExpression("x^3 - 3*x^2").ToDelegate("x");
double val = func(45);
$$anonymous$$eep in $$anonymous$$d that you can derive a custom parser from ExpressionParser and add more custom methods / "constants":
public class $$anonymous$$yParser : ExpressionParser
{
public $$anonymous$$yParser()
{
AddConst("time",()=>Time.time);
}
}
var parser = new $$anonymous$$yParser();
double time2 = parser.Evaluate("time * 2");
Any other "usage helpers" depends on the usage you need. For example if you want to be able to pass a variable amount of parameters and values, the values also need to be passed as string as you can only have 1 params array. Example:
public static float Eval(string aExpression, params string[] aParams)
{
if (aParams.Length %2 != 0)
return 0; // wrong parameter count
var exp = Expression.Parse(aExpression);
for(int i = 0; i < aParams.Length; i+=2)
{
exp.Parameters[aParams[i]].Value = double.Parse(aParams[i+1]);
}
return (float)exp.Value;
}
This could be used like that:
float v = Eval("sqrt(x^2+y^2)","x","5","y","6");
So the method takes pairs of a parameter name and the parameter value as string. However i don't see a good usage for this. If you pass wrong things to the parameter names / values you most likely will get an exception of some sort.
Answer by AlwaysSunny · Nov 16, 2014 at 03:10 PM
If I understand what you're saying, it's a pretty complex topic. (Parsing) First you decide upon the rules by which the user can enter their string o' commands, operators, variables, then code a solution which analyzes a string, breaking those components into individual commands, operators, and variables to create a sensible operation. Research parsing, string analysis, and regular expressions. Probably'll need to use hashtables also for passing around your freshly parsed elements.
If you are willing to use unityscript as opposed to C# then you can use the built in eval command:
var x : float;
var f : String = "x*x";
function Update () {
if(Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.Q)){
Debug.Log(eval(f));
}
}
I found quite nice way in this question: http://answers.unity3d.com/questions/34349/how-to-evaluate-a-mathematical-expression-in-unity.html where I replace x with numbers:
public static double Evaluate(string independentArgument, string expression, string argumentValue)
{
var doc = new System.Xml.XPath.XPathDocument(new System.IO.StringReader("<r/>"));
var nav = doc.CreateNavigator();
var newString = expression;
newString = (new System.Text.RegularExpressions.Regex(@"([\+\-\*])")).Replace(newString, " ${1} ");
newString = newString.Replace("/", " div ").Replace("%", " mod ").Replace(independentArgument, argumentValue);
return (double)nav.Evaluate("number(" + newString + ")");
}
It works quite well, but I can't access more complicated functions, like Abs(), Pow() or Exp()
Can anyone recognise if it is calculated through X$$anonymous$$L language or something else?
It seems to work quite well with simple algebra but there is still one big question outstanding: how to use more complicated functions. This involves:
Is it accessing through X$$anonymous$$L language and calculating in C# ? If so, maybe it is not very difficult to use C# functions?
If it's not possible, maybe more complex functions can be approximated with simple ones, i.e. using Taylor expansion, etc.
These are open questions, which should be good to cover to close this thread.
@chanfort: The XPath classes provides a framework to query an X$$anonymous$$L document with XPath. XPath is not part of X$$anonymous$$L itself. However as far as i know all those classes are bundled in the System.Xml assembly which is quite large (since it contains a lot classes) and will significantly increase your code-base size. Unless you use the X$$anonymous$$L classes anyways or you don't care about the size you can use the expression parser that's part of the XPath framework.
It is possible to add custom methods to the XPath parser, though it's quite complicated. XPath is ment to be used to process X$$anonymous$$L documents.
Your answer
Follow this Question
Related Questions
Gui list and color 0 Answers
Input keycode string not working 2 Answers
Help In Making a SphereCast for 3D Tire! Working RayCast Script included! 0 Answers
Using Input.GetJoystickNames 1 Answer