- Home /
Create a seed or string to run multiple methods. C#
Hey, I want to create a script that reads a string and executes methods in order of where certain characters are places, characters that would represent methods. C# specifically here. So for example let's say I have a string that says "A3:E:A2". The idea here is that "A" would represent a method, "3" represents a value that is the "A" method needs and the ":" is a place for the script to know where to stop reading and execute this method. "E" is the next method and as it doesn't need a value so I didn't add a number to the string.
So the alphabetical letters are methods, and numbers in this case are values that are needed for the methods, perhaps later I could add a character for the reader to understand where to read a new numerical value if a method has multiple values that needs input.
So yeah, how do I read a string and run methods based on the characters?
Answer by Bunny83 · May 18, 2021 at 02:15 PM
Since we have quite a few implementations already, I just want to add my LogicExpressionParser which is an improved version of my old ExpressionParser. It can actually parse maths equations that may be fed into custom functions. The LogicExpressionParser has the addition of boolean operators and boolean expressions.
Of course a single expression is evaluated "at once", so there is no real chaining of methods. However if you just split your string into individual function calls you can actually utilize the full power of the parser. So registrated functions can take multiple arguments (only numbers, doubles to be more precise) and the arguments could be a mathematical expression. It also provides support for variables.
Note that the parser is not a scripting language. There's no assignment operator and no control structures. Though it may simplify the parsing of a function and its parameters / arguments. Though you would actually use an actual function call syntax. So it may look like this
"A(3):E():A(2)"
As said you still need to split your string at the colons before parsing each part. Though you could even parse something like
"A(3+x):E(someVar * 3 + 5):A(2)"
Of course your code has to set the variable "x" and "someVar" but it will be picked up and used when the methods are called. In the end each part would just yield an expression that when evaluated will call the method as a side effect ^^. You can register / add as many functions as you like to the parsing context of the parser. Each parsed expression an expression context which manages the variables for a parsed expression. By default that context is shared between multiple expressions. The parsed expression has some convenient indexers to simplify the setting of variables.
So here's a simple example. Of course, after we added the file to the project we have to include the namespace "B83.LogicExpressionParser" when we want to use the Parser.
using B83.LogicExpressionParser;
// [ ... ]
Parser parser;
void Awake()
{
parser = new Parser();
parser.ParsingContext .AddFunction("A", MethodA);
parser.ParsingContext .AddFunction("E", MethodE);
}
double MethodA(ParameterList aParams)
{
Debug.Log("Method A: " + aParams[0]);
return 0;
}
double MethodE(ParameterList aParams)
{
Debug.Log("Method E");
return 0;
}
List<LogicExpression> ParseCommandString(aString)
{
var parts = aString.Split(':');
var result = new List<LogicExpression>();
foreach(var part in parts)
{
result.Add(parser.Parse(part));
}
return result;
}
So this would be the basic outline. You can now call "ParseCommandString" with your command string like this
List<LogicExpression> list = ParseCommandString("A(3):E():A(2)");
and you get a list of all individual expressions that can be evaluated at will. For example just doing this:
foreach(var exp in list)
{
exp.GetResult();
}
Will execute the methods specified in the string in order one after another.
In order to use variables you can just set the value of those variable before you call GetResult on an expression:
exp["x"] = 5;
exp.GetResult();
This will use the value 5 in place where "x" was used in the expression. Since by default all expressioned parsed by the same Parser share the same expression context, you can set the values for the variables once
parser.ExpressionContext["x"] = 5;
Since the custom expression tree internally references the variable in the context, changing a variable and calling GetResult again will use the new value.
Though I guess this was all out of scope of the original question ^^. I've written the LogicExpressionParser for the parametric L-System I've written for this question in order to parse the conditions, just if someone is interested.
This looks actually incredible. I've definitely wanted to write something similar to this that could do simple algebraic expressions but never found the time. Thanks for this, as always Bunny!
:) I just like to play around and see how certain problems could be solved. A long time ago I made a webGL (originally a UnityWebPlayer) build of the old expression parser. You can type in any expression and it will draw a continuous line based on the two equations provided. Any unknown variable will be fed with a value between -1 and 1. I think I did 2000 steps. So the two expressions are evaluated 2000 times per frame to produce a line strip. There are other variables like "time" which is just Time.time which is useful for anything animated. I also added most Mathf functions. There are a few constants like "e" or "PI" (case matters). The screen area represents an area between -1 and 1 in both axes.
I just tried it, omg.. You literally just created a more interesting Desmos.com !
I'm sure this could be very useful but it's way out of my level. I don't think I can use something I don't fully understand in the long term. I hope other people can look a this for their projects and find things for them to use. So I'll upvote it regardless.
Answer by Larry-Dietz · Nov 27, 2019 at 02:18 PM
I am writing this off the top of my head directly in the answer, so it hasnt been tested, but should get you on the right track. This is the easiest was I could think to do this.
string CommandString="A3:E:A2";
public void ParseCommands()
{
string[] Commands = CommandString.Split(':');
foreach (String s in commands)
{
switch (s[0])
{
case "A":
MethodA(int.parse(S[1]);
break;
Case "E":
MethodE();
break;
}
}
}
This should at least get you a start.
I wont say this is the absolute best way to do this, but it is the easiest way I could think of.
Hope it helps, -Larry
Hello! I'm now in a situation with more time. Will The $$anonymous$$ethodA(Int.parse(s([1])); parse multiple digits or just 1-9? I assume it would only take in 1-9 but I'm not quite sure. Also, wouldn't the 1 in the parse be incorrect here as I assume A3 is part of the same string?
Answer by Llama_w_2Ls · May 18, 2021 at 09:07 AM
Here's my implementation, using a dictionary to store delegates instead. This scales better than a large switch-case structure if you've got a lot of methods:
class MethodString
{
public readonly string CommandString;
delegate void Command(int i);
Dictionary<char, Command> Commands;
public MethodString(string seed)
{
CommandString = seed;
InitializeActions();
CallActions();
}
void InitializeActions()
{
Commands = new Dictionary<char, Command>()
{
{ 'A', MethodA },
{ 'E', MethodE }
};
}
void CallActions()
{
string[] commands = CommandString.Split(':');
foreach (var command in commands)
{
var action = Commands[command[0]];
action.Invoke(int.Parse(command[1].ToString()));
}
}
void MethodA(int i)
{
// Do smt
Console.WriteLine(i + ": from A");
}
void MethodE(int i)
{
// Do smt else
Console.WriteLine(i + ": from E");
}
}
And you call it like this:
void Start()
{
new MethodString("A3:E2:A4");
}
Wow, I don't even know how most of this works. I'm going to have to read up.
Wow, I don't even know how most of this works. I'm going to have to read up. I remember delegates but I don't really remember what it is. It was a while since I did coding last time I made this thread and now it's been a bit of time again. Never used a dictionary either.
Sorry if it was hard to understand:
A delegate is a data type that essentially allows you to take in methods as parameters to a function or class constructor, which I use to fill in the dictionary.
Dictionaries are kinda like lists but function totally differently. They use a hashtable structure to store data, which is storing the hashcode of a unique key, which points to data (also known as a key-value pair).
For example:
var myDictionary = new Dictionary<int, string>();
myDictionary.Add(0, "Hello"); // adds a new key-value pair with key '1' and value 'Hello'.
print(myDictionary[0]); // Prints hello
myDictionary.Add(0, "Bye"); // error - the dictionary already contains the key '0'. DIctionaries have to have unique keys
myDictionary.Add(1, "Hello") // this is fine - dictionaries can contain duplicate or null values
By using this data structure, dictionaries have extremely fast lookup times of O(1)
, instead of O(n)
, which is the worst case lookup speed of a list. That's why I encourage you to use dictionaries in cases where you have keys that could point to data, such as a name points towards a game object.
Hope that clarifies things :)
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
String.Remove not working 1 Answer