[英]Evaluate logic expression contained in a passed in string based on a list of strings
So I am unsure how to word this question properly.所以我不确定如何正确地表达这个问题。
If you look below lets say I have a list of options (PG, PA, PM, TK, TD) that a customer has ordered.如果您看下面,假设我有一个客户订购的选项列表(PG、PA、PM、TK、TD)。 Now lets say I have some expression I need evaluate against the customers ordered options such as: PA OR PB where evaluates to customers list of options contains either option PA or PB.
现在假设我有一些表达式需要根据客户订购的选项进行评估,例如:PA 或 PB,其中对客户选项列表的评估包含选项 PA 或 PB。 Seems simple enough but these expressions could grow quite a bit.
看起来很简单,但这些表达式可能会增长很多。 Below in the code are some good examples of what I would like to accomplish.
下面的代码是我想要完成的一些很好的例子。
I by no means claim to have knowledge about string parsing and comparing logical operations.我绝不声称拥有有关字符串解析和比较逻辑操作的知识。 I am looking for some general direction or what my options are.
我正在寻找一些总体方向或我的选择是什么。 I saw some things about dynamic linq, rule engines, expression trees, etc. Its just a whole lot to absorb and am looking for some direction on which would accomplish what I want?
我看到了一些关于动态 linq、规则引擎、表达式树等的东西。需要吸收的东西太多了,我正在寻找可以实现我想要的东西的方向?
I am open to just about any approach.我对任何方法都持开放态度。 Any answers are appreciated!
任何答案表示赞赏!
Code:代码:
class Program
{
static void Main(string[] args)
{
//Reprsents what the customer selected
List<string> CustomerSelectedOptins = new List<string> { "PG", "PA", "PM", "TK", "TD" };
string LogicOperation = "PA OR (PB AND PC)"; //Evaluates true since customer list contains PA
string LogicOperation2 = "PF OR (NOT PB AND PM)"; //Evaluates true since customer list does not contain PB but contain PM
string LogicOperation3 = "NOT PG AND NOT(PL AND TZ)"; //Evaluates false since the customer does have PG selected
}
}
There are a few different approaches you can take.您可以采用几种不同的方法。 One common one is to replace the option checks with
true
or false
and then use one of the built-in expression evaluators to evaluate the resulting expression.一种常见的方法是用
true
或false
替换选项检查,然后使用内置表达式计算器之一来计算结果表达式。 Note: XOR
is not available for these.注意:
XOR
不适用于这些。
public static class Evaluator {
static Regex wordRE = new Regex(@"[A-Z]+", RegexOptions.Compiled);
static HashSet<string> Operators = new[] { "AND", "OR", "NOT" }.ToHashSet(StringComparer.OrdinalIgnoreCase);
public static bool Evaluate(this List<string> options, string op) {
var opListOfOptions = wordRE.Matches(op).Select(m => m.Value).Where(w => !Operators.Contains(w));
foreach (var option in opListOfOptions) {
var value = options.Contains(option).ToString();
op = op.Replace(option, value);
}
//return DTEval(op) == 1;
return CompEval(op);
//return XEval(op);
}
static double DTEval(string expression) {
var dt = new DataTable();
var loDataColumn = new DataColumn("Eval", typeof(double), expression);
dt.Columns.Add(loDataColumn);
dt.Rows.Add(0);
return (double)(dt.Rows[0]["Eval"]);
}
static DataTable cDT = new DataTable();
static bool CompEval(string expression) {
return (bool)cDT.Compute(expression, "");
}
public static bool XEval(string expression) {
expression = new System.Text.RegularExpressions.Regex(@"not +(true|false)").Replace(expression.ToLower(), " not(${1}) ");
expression = new System.Text.RegularExpressions.Regex(@"(true|false)").Replace(expression, " ${1}() ");
return (bool)new System.Xml.XPath.XPathDocument(new System.IO.StringReader("<r/>")).CreateNavigator()
.Evaluate(String.Format("boolean({0})", expression));
}
}
The Evaluate
method comments show the different options you could use. Evaluate
方法注释显示了您可以使用的不同选项。
Alternatively, you could write your own expression evaluator.或者,您可以编写自己的表达式计算器。 A simple one is a recursive descent evaluator that parses a simple grammar.
一个简单的是解析简单语法的递归下降评估器。 This one uses C# precedence rules, having OR/XOR/AND binding left to right.
这个使用 C# 优先规则,从左到右具有 OR/XOR/AND 绑定。
public class Evaluator {
// recursive descent boolean expression evaluator
// grammer:
// primary = id
// primary = ( expr )
// unop = primary
// unop = not unop
// andop = unop [ and unop ]*
// xorop = andop [ xor andop ]*
// orop = xorop [ or xorop ]*
// expr = orop
public class TokenList {
List<string> tokens;
int curTokenNum;
static Regex tokenRE = new Regex(@"\w+|[()]", RegexOptions.Compiled);
public TokenList(string expr) {
curTokenNum = 0;
tokens = tokenRE.Matches(expr).Select(m => m.Value).ToList();
}
public string CurToken => curTokenNum < tokens.Count ? tokens[curTokenNum] : String.Empty;
public void MoveNext() => ++curTokenNum;
public bool MoreTokens => curTokenNum < tokens.Count;
public void NextToken() {
MoveNext();
if (!MoreTokens)
throw new InvalidExpressionException("Expected token");
}
}
static List<string> OperatorStrings = new[] { "AND", "OR", "XOR", "NOT" }.ToList();
enum Operators { and, or, xor, not };
static List<string> ParenStrings = new[] { "(", ")" }.ToList();
enum Parens { open, close };
TokenList tokens;
List<string> trueOptions;
public Evaluator(List<string> trueOptions) {
this.trueOptions = trueOptions;
}
string curToken => tokens.CurToken;
bool curTokenValue => trueOptions.Contains(curToken);
bool isOperator => OperatorStrings.FindIndex(s => s.Equals(curToken, StringComparison.OrdinalIgnoreCase)) != -1;
Operators curOp => (Operators)OperatorStrings.FindIndex(s => s.Equals(curToken, StringComparison.OrdinalIgnoreCase));
bool isParen => ParenStrings.Contains(curToken);
Parens curParen => (Parens)(ParenStrings.IndexOf(curToken));
public bool id() {
if (isOperator)
throw new InvalidExpressionException("missing operand");
else {
var ans = curTokenValue;
tokens.MoveNext();
return ans;
}
}
bool primary() {
if (isParen)
if (curParen == Parens.open) {
tokens.NextToken();
var ans = expr();
if (!isParen || curParen != Parens.close)
throw new InvalidExpressionException($"missing ) at {curToken}");
else
tokens.MoveNext();
return ans;
}
else
throw new InvalidExpressionException("Invalid )");
else
return id();
}
bool unop() {
if (isOperator && curOp == Operators.not) {
tokens.NextToken();
return !unop();
}
else
return primary();
}
bool andop() {
var ans = unop();
while (tokens.MoreTokens && isOperator && curOp == Operators.and) {
tokens.NextToken();
ans = ans & unop();
}
return ans;
}
bool xorop() {
var ans = andop();
while (tokens.MoreTokens && isOperator && curOp == Operators.xor) {
tokens.NextToken();
ans = ans ^ andop();
}
return ans;
}
bool orop() {
var ans = xorop();
while (tokens.MoreTokens && isOperator && curOp == Operators.or) {
tokens.NextToken();
ans = ans | xorop();
}
return ans;
}
bool expr() => orop();
public bool Value(string exp) {
tokens = new TokenList(exp);
var ans = expr();
if (tokens.MoreTokens)
throw new InvalidExpressionException($"Unrecognized token {curToken} after expression");
return ans;
}
}
You can call it by creating an Evaluator
and passing it an expression to evaluate:您可以通过创建
Evaluator
并将其传递给评估表达式来调用它:
var eval = new Evaluator(CustomerSelectedOptions);
var ans = eval.Value(LogicOperation);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.