簡體   English   中英

根據字符串列表評估傳入字符串中包含的邏輯表達式

[英]Evaluate logic expression contained in a passed in string based on a list of strings

所以我不確定如何正確地表達這個問題。

如果您看下面,假設我有一個客戶訂購的選項列表(PG、PA、PM、TK、TD)。 現在假設我有一些表達式需要根據客戶訂購的選項進行評估,例如:PA 或 PB,其中對客戶選項列表的評估包含選項 PA 或 PB。 看起來很簡單,但這些表達式可能會增長很多。 下面的代碼是我想要完成的一些很好的例子。

我絕不聲稱擁有有關字符串解析和比較邏輯操作的知識。 我正在尋找一些總體方向或我的選擇是什么。 我看到了一些關於動態 linq、規則引擎、表達式樹等的東西。需要吸收的東西太多了,我正在尋找可以實現我想要的東西的方向?

我對任何方法都持開放態度。 任何答案表示贊賞!

代碼:

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


    }
}

您可以采用幾種不同的方法。 一種常見的方法是用truefalse替換選項檢查,然后使用內置表達式計算器之一來計算結果表達式。 注意: 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));
    }
}

Evaluate方法注釋顯示了您可以使用的不同選項。

或者,您可以編寫自己的表達式計算器。 一個簡單的是解析簡單語法的遞歸下降評估器。 這個使用 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;
    }
}

您可以通過創建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