简体   繁体   English

根据字符串列表评估传入字符串中包含的逻辑表达式

[英]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.一种常见的方法是用truefalse替换选项检查,然后使用内置表达式计算器之一来计算结果表达式。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM