从字符串描述构建布尔函数

时间:2011-08-31 07:39:26

标签: c# algorithm parsing expression-evaluation

我有一个庞大的布尔值数据库,并希望构建一个框架,以便在所有值上轻松运行查询。为此,我想编写一个函数,给定一个布尔表达式的字符串表示,将在数据库的所有元素上计算该表达式。例如,给定输入

(a && b) || c

该函数将构建另一个将评估

的函数
return (funcA() && funcB()) || funcC();

其中funcAfuncBfuncC是返回布尔值的函数

5 个答案:

答案 0 :(得分:5)

这似乎最好分三步完成。

首先,你需要弄清楚你应该评估什么。这通常分两步完成,分别称为扫描解析。扫描的工作是将输入字符串分解为一系列标记,组成文本的较小逻辑单元。例如,给定字符串

(a && b)

你会将其分解为代币

(
a
&&
b
)

通常,这是使用正则表达式完成的,但您也可以手动完成。主要思想是将确定字符串片段的任务与查看这些片段相关的任务分开。

扫描完输入后,需要对其进行解析以确定所说的内容。也就是说,您将令牌重新组合成一个完整的数学表达式编码运算符优先级,正在使用的操作数等等。有很多算法可以做到这一点,但也许最简单的算法是Dijkstra的 shunting yard algorithm < / strong>,这很容易实现。您可能会使用抽象语法树来存储此解析步骤的输出,这是一种编码输入结构的树结构。

此时,您对要评估的表达式的含义有明确的解释,您需要对其进行实际评估!为此,您可能会为每个AST节点定义一些从该节点生成值的函数。对于像&amp;&amp;这样的运算符,您将评估左右子表达式,然后计算它们的AND(或者如果lhs为假则可能使用短路来避免计算rhs)。对于单个字母,您可以使用反射来调用相应的方法,或者可以将表映射到函数的表(取决于您想要的安全性。)

作为编码方面的潜在优化,您可能需要考虑省略AST的构造,并且只需计算您想要的值。分流码算法(以及许多其他解析器,例如自上而下的LL(1)或自下而上的LR(1)解析器)通常允许您根据其组成表达式计算表达式的某些总值,并且可能更容易以这种方式编码。但是,如果您计划在数据库之类的大型数据集上使用所描述的函数,那么计算AST将为您提供一个对象,您可以调用数据库中的每个值来生成您想要的值。

如果您计划对大量数据运行大规模复杂查询,您甚至可能希望更进一步,实际将生成的表达式转换为C#代码,然后编译并加载到正在运行的程序中。我已经在Java中看到过这样的例子,但这是一个非常高性能的应用程序,除非你已经用尽所有其他选项,否则这可能是过度的。

希望这有帮助!

答案 1 :(得分:2)


好的,这是我选择的解决方案。

我使用以下codeproject

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

我获得了标志和规则ID列表 例如: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

答案 2 :(得分:1)

你有括号,所以你必须为需要首先评估的子表达式解析它(递归地,在堆栈上,无论如何)。您必须解析运算符(&amp;&amp;,||,!)以及符号(a,b,c),并用适当的逻辑运算符或函数调用替换它们。

让你开始:

除非您从符号开始,否则您将以符号开头!运营商。

如果您以符号开头,则下一个字符最好是二元运算符(&amp;&amp;,||)。之后的角色更好地是子表达式或符号。如果它是子表达式,则递归计算它。如果它是一个符号,请关闭哪个操作符位于中间,然后根据需要将它们或或它们组合在一起,然后返回该值。

答案 3 :(得分:1)

您可以通过解析输入字符串然后使用反射创建要执行和执行它们的方法来实现此目的,但这是一个相当复杂的解决方案。你到底想要完成什么?使用lambdas和表达式树和委托可能有更好的方法。

答案 4 :(得分:0)

我认为这可以使用.NET反射完成而不是进入解析的细节(因为我看到了C#标签,我希望这个解决方案没问题)。 使用反映给定表达式的反射emit a method,然后调用此方法获取结果。我个人认为为此编写解析器比使用.NET反射更难,更耗时。