开发者

Building a boolean function from a string description

开发者 https://www.devze.com 2023-04-01 09:10 出处:网络
I have a large database of boolean values and want to build a framework for easily running queries over all of the values.To do this, I\'d like to write a function that, given a string representation

I have a large database of boolean values and want to build a framework for easily running queries over all of the values. To do this, I'd like to write a function that, given a string representation of a boolean expression, would evaluate that expression over all of the elements of the database. For example, given input

(a && b) || c

The function would construct another function that would evaluate

return (funcA() && funcB()) || funcC();

where funcA, funcB, an开发者_运维问答d funcC are functions returning booleans


This seems like it is best done in three steps.

First, you need to figure out what exactly you're supposed to evaluate. This is usually done in two steps called scanning and parsing. The job of scanning is to break the input string into a sequence of tokens, smaller logical units that make up the text. For example, given the string

(a && b)

You would break this into the tokens

(
a
&&
b
)

Typically, this is done using regular expressions, though you can do it by hand as well. The main idea is to separate the task of determining the pieces of the string from the task of seeing how those pieces relate.

Once you've scanned the input, you need to parse it to determine what is being said. That is, you will reassemble the tokens into a complete mathematical expression encoding operator precedence, what operands are being used, etc. There are many algorithms to do this, but perhaps the easiest of them is Dijkstra's shunting yard algorithm, which is fairly easy to implement. You would likely store the output of this parsing step using an abstract syntax tree, a tree structure encoding the structure of the input.

At this point, you have an unambiguous interpretation of the meaning of the expression to evaluate and you'll need to actually evaluate it! To do this, you would probably define, for each AST node, some function to produce a value from that node. For operators like &&, you would evaluate the left and right subexpressions and then compute their AND (or perhaps use short-circuiting to avoid computing the rhs if the lhs is false). For individual letters, you'd use reflection to invoke the corresponding method, or could have a table mapping names to functions (depending on the security you want.)

As a potential optimization in terms of coding, you may want to consider omitting construction of the AST and to just compute the values you want as you go. The shunting-yard algorithm (and many other parsers, such as a top-down LL(1) or bottom-up LR(1) parser) usually let you compute some overall value for an expression in terms of its constituent expressions, and it may be easier to code up this way. However, if you're planning on using the described function over a huge data set like a database, computing the AST would give you an object that you could invoke on each value in the database to produce the values you'd like.

If you are planning on running massively complex queries over a huge set of data, you may even want to go one step further and actually convert the generated expression down to C# code that you would then compile and load into the running program. I've seen examples in Java where this was used to great effect, but this was for a very-high performance application and is probably overkill unless you've exhausted all other options.

Hope this helps!



OK here is my selected solution.

I use the following codeproject

http://www.codeproject.com/KB/dotnet/Expr.aspx

I get list of Signs and Rule Ids for example:ArgsList = List<string> ={"0","&&","5"} // (0&&5)

   int id;
   var tmp = new List<string>();
   //------------------------------//
   foreach( string arg in ArgsList)
   {
       if( ( arg != "&&" && arg != "||" && arg != ")" && arg != "(" ) )
       {
          try
          {
              id = int.Parse(arg);
          }
          catch( Exception ex )
          {
               return false;
          }
          tmp.Add(GetRuleById(id, ref errorString).Check(wwObject, ref errorString).ToString());
       }
       else
       {
            tmp.Add(arg);
       }
  }

  //foreach converts it to List<string> = {"True","&&","False"}
  string stringtoeval;
  stringtoeval = string.Join(string.Empty, tmp.ToArray()).ToLower();//"True&&False"
  return (bool)EvalCSCode.EvalCSCode.Eval(stringtoeval);//returns false


You can accomplish this by parsing the input string and then using reflection to create the methods you want to execute and execute them, but this is a rather involved solution. What exactly are you trying to accomplish with this? There may be a better way to do it using lambdas and expression trees and delegates.


You've got parentheses, so you'll have to parse it (recursively, on a stack, whatever) for sub-expressions that need to be evaluated first. You'll have to parse for operators (&&, ||, !) as well as symbols (a, b, c), and replace them with the appropriate logical operators or function calls.

To start you off:

You'll start with a symbol unless you start with the ! operator.

If you start with a symbol, the next character better be a binary operator (&&, ||). And the character after that better be a subexpression or a symbol. If it's a subexpression, evaluate it recursively. If it's a symbol, switch off of which operator was in the middle and AND or OR them together as appropriate, and return the value.


Instead of going into the details of parsing, I think this can be done using .NET reflection (since I see the C# tag, I hope this solution is OK). Using reflection emit a method that evaluates a given expression and then call this method to get the result. I personally feel that writing a parser for this is tougher and more time consuming than using .NET reflection.

0

精彩评论

暂无评论...
验证码 换一张
取 消