एक स्ट्रिंग को उसके समतुल्य LINQ अभिव्यक्ति वृक्ष में कैसे परिवर्तित करें?


173

यह मूल समस्या का एक सरलीकृत संस्करण है।

मेरे पास व्यक्ति नामक एक वर्ग है:

public class Person {
  public string Name { get; set; }
  public int Age { get; set; }
  public int Weight { get; set; }
  public DateTime FavouriteDay { get; set; }
}

... और एक उदाहरण देता है:

var bob = new Person {
  Name = "Bob",
  Age = 30,
  Weight = 213,
  FavouriteDay = '1/1/2000'
}

मैं अपने पसंदीदा पाठ संपादक में एक स्ट्रिंग के रूप में निम्नलिखित लिखना चाहूंगा ...।

(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3

मैं इस स्ट्रिंग और मेरे ऑब्जेक्ट इंस्टेंस को लेना चाहता हूं और ऑब्जेक्ट उदाहरण पर एक TRUE या FALSE का मूल्यांकन करता हूं - अर्थात फंक <व्यक्ति, बूल> का मूल्यांकन करना।

यहाँ मेरे वर्तमान विचार हैं:

  1. बुनियादी तुलना और तार्किक ऑपरेटरों का समर्थन करने के लिए ANTLR में एक बुनियादी व्याकरण को लागू करें। मैं विज़ुअल बेसिक मिसाल और यहाँ कुछ सुविधाओं को कॉपी करने की सोच रहा हूँ: http://msdn.microsoft.com/en-us/library/fw84t893(VS.80).aspx
  2. उपलब्ध स्ट्रिंग से ANTLR एक उपयुक्त एएसटी बनाएं।
  3. एएसटी वॉक करें और प्रेडिकेटेट बिल्डर का उपयोग करें गतिशील रूप से फंक <व्यक्ति, बूल> बनाने के लिए फ्रेमवर्क का
  4. आवश्यकतानुसार व्यक्ति के उदाहरण के खिलाफ विधेय का मूल्यांकन करें

मेरा सवाल यह है कि क्या मैंने इस पर पूरी तरह से काबू पा लिया है? कोई विकल्प?


संपादित करें: चुना समाधान

मैंने डायनेमिक Linq लाइब्रेरी का उपयोग करने का निर्णय लिया, विशेष रूप से LINQSamples में उपलब्ध डायनेमिक क्वेरी क्लास।

नीचे कोड:

using System;
using System.Linq.Expressions;
using System.Linq.Dynamic;

namespace ExpressionParser
{
  class Program
  {
    public class Person
    {
      public string Name { get; set; }
      public int Age { get; set; }
      public int Weight { get; set; }
      public DateTime FavouriteDay { get; set; }
    }

    static void Main()
    {
      const string exp = @"(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3";
      var p = Expression.Parameter(typeof(Person), "Person");
      var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, exp);
      var bob = new Person
      {
        Name = "Bob",
        Age = 30,
        Weight = 213,
        FavouriteDay = new DateTime(2000,1,1)
      };

      var result = e.Compile().DynamicInvoke(bob);
      Console.WriteLine(result);
      Console.ReadKey();
    }
  }
}

परिणाम प्रकार System.Boolean का है, और इस उदाहरण में TRUE है।

मार्क ग्रेवल को बहुत धन्यवाद।

शामिल System.Linq.Dynamic nuget पैकेज, प्रलेखन यहाँ


33
अपने प्रश्न के साथ पूर्ण समाधान कोड पोस्ट करने के लिए धन्यवाद। बहुत सराहना की।
एड्रियन ग्रिगोर

क्या होगा यदि आपके पास एक संग्रह या लोग हैं और कुछ तत्वों को फ़िल्टर करना चाहते हैं? व्यक्ति। ए। जी। ३ और व्यक्ति। ५०> ५०?
सेहियो

धन्यवाद। मुझे डायनामिकएक्सप्रेशन.पर्सलम्बडा () नहीं मिल रहा है। यह किस विधानसभा और विधानसभा में है?
मैट फिट्जमोरिस

सभी अच्छे .. नामस्थानों के बीच एक अस्पष्टता थी। जरूरत है - E = System.Linq.Expressions का उपयोग करते हुए; System.Linq.Dynamic का उपयोग कर;
मैट फिट्जमोरिस

यह 'और' के बजाय 'और' का उपयोग क्यों करता है क्या यह C # कोड नहीं है?
त्रिंको

जवाबों:


65

क्या डायनामिक लिनक लाइब्रेरी यहां मदद करेगी ? विशेष रूप से, मैं एक Whereखंड के रूप में सोच रहा हूं । यदि आवश्यक हो, तो इसे कॉल करने के लिए बस एक सूची / सरणी के अंदर रखें .Where(string)! अर्थात

var people = new List<Person> { person };
int match = people.Where(filter).Any();

यदि नहीं, तो एक पार्सर लिखना ( Expressionहुड के नीचे का उपयोग करना ) बहुत अधिक कर नहीं है - मैंने अपनी ट्रेन में क्रिसमस के ठीक पहले एक समान (हालांकि मुझे नहीं लगता कि मेरे पास स्रोत है) लिखा था ...


मार्क का मतलब क्या है "पार्सर लिखना (हुड के नीचे अभिव्यक्ति का उपयोग करके)" पार्स करना और फिर एक अभिव्यक्ति ट्री बनाना, या System.Linq.Expressions में कुछ पार्सिंग तंत्र है?
AK_

मुझे पूरा यकीन है कि वह एक फाइल में अभिव्यक्ति के रूप में इस तरह से पढ़ना चाहता है और फिर इसे एक विधेय और संकलित के रूप में अनुवादित किया जाना चाहिए। प्रश्न यह प्रतीत होता है कि, अपने व्याकरण को 'स्ट्रिंग' से 'प्रिडिक्ट' में बदलना है। // Lambda expression as data in the form of an expression tree. System.Linq.Expressions.Expression<Func<int, bool>> expr = i => i < 5; // Compile the expression tree into executable code. Func<int, bool> deleg = expr.Compile(); // Invoke the method and print the output. Console.WriteLine("deleg(4) = {0}", deleg(4)); ParseLambda अच्छा है!
लेटेंसी

31

ऐसी ही एक और लाइब्रेरी है फ्ले

मैंने डायनेमिक लिनेक लाइब्रेरी और फ्ली की त्वरित तुलना की और फ्ले की अभिव्यक्ति के लिए 10 गुना तेज था"(Name == \"Johan\" AND Salary > 500) OR (Name != \"Johan\" AND Salary > 300)"

इस तरह से आप अपने कोड को Flee का उपयोग करके लिख सकते हैं।

static void Main(string[] args)
{
  var context = new ExpressionContext();
  const string exp = @"(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3";
  context.Variables.DefineVariable("Person", typeof(Person));
  var e = context.CompileDynamic(exp);

  var bob = new Person
  {
    Name = "Bob",
    Age = 30,
    Weight = 213,
    FavouriteDay = new DateTime(2000, 1, 1)
  };

  context.Variables["Person"] = bob;
  var result = e.Evaluate();
  Console.WriteLine(result);
  Console.ReadKey();
}

शायद मुझे कुछ याद आ रहा है, लेकिन 'लाइन' कैसे एक लाइनक एक्सप्रेशन ट्री बनाने में मदद करता है?
माइकल बी हिल्डेब्रांड

9
void Main()
{
    var testdata = new List<Ownr> {
        //new Ownr{Name = "abc", Qty = 20}, // uncomment this to see it getting filtered out
        new Ownr{Name = "abc", Qty = 2},
        new Ownr{Name = "abcd", Qty = 11},
        new Ownr{Name = "xyz", Qty = 40},
        new Ownr{Name = "ok", Qty = 5},
    };

    Expression<Func<Ownr, bool>> func = Extentions.strToFunc<Ownr>("Qty", "<=", "10");
    func = Extentions.strToFunc<Ownr>("Name", "==", "abc", func);

    var result = testdata.Where(func.ExpressionToFunc()).ToList();

    result.Dump();
}

public class Ownr
{
    public string Name { get; set; }
    public int Qty { get; set; }
}

public static class Extentions
{
    public static Expression<Func<T, bool>> strToFunc<T>(string propName, string opr, string value, Expression<Func<T, bool>> expr = null)
    {
        Expression<Func<T, bool>> func = null;
        try
        {
            var type = typeof(T);
            var prop = type.GetProperty(propName);
            ParameterExpression tpe = Expression.Parameter(typeof(T));
            Expression left = Expression.Property(tpe, prop);
            Expression right = Expression.Convert(ToExprConstant(prop, value), prop.PropertyType);
            Expression<Func<T, bool>> innerExpr = Expression.Lambda<Func<T, bool>>(ApplyFilter(opr, left, right), tpe);
            if (expr != null)
                innerExpr = innerExpr.And(expr);
            func = innerExpr;
        }
        catch (Exception ex)
        {
            ex.Dump();
        }

        return func;
    }
    private static Expression ToExprConstant(PropertyInfo prop, string value)
    {
        object val = null;

        try
        {
            switch (prop.Name)
            {
                case "System.Guid":
                    val = Guid.NewGuid();
                    break;
                default:
                    {
                        val = Convert.ChangeType(value, prop.PropertyType);
                        break;
                    }
            }
        }
        catch (Exception ex)
        {
            ex.Dump();
        }

        return Expression.Constant(val);
    }
    private static BinaryExpression ApplyFilter(string opr, Expression left, Expression right)
    {
        BinaryExpression InnerLambda = null;
        switch (opr)
        {
            case "==":
            case "=":
                InnerLambda = Expression.Equal(left, right);
                break;
            case "<":
                InnerLambda = Expression.LessThan(left, right);
                break;
            case ">":
                InnerLambda = Expression.GreaterThan(left, right);
                break;
            case ">=":
                InnerLambda = Expression.GreaterThanOrEqual(left, right);
                break;
            case "<=":
                InnerLambda = Expression.LessThanOrEqual(left, right);
                break;
            case "!=":
                InnerLambda = Expression.NotEqual(left, right);
                break;
            case "&&":
                InnerLambda = Expression.And(left, right);
                break;
            case "||":
                InnerLambda = Expression.Or(left, right);
                break;
        }
        return InnerLambda;
    }

    public static Expression<Func<T, TResult>> And<T, TResult>(this Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, TResult>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
    }

    public static Func<T, TResult> ExpressionToFunc<T, TResult>(this Expression<Func<T, TResult>> expr)
    {
        var res = expr.Compile();
        return res;
    }
}

LinqPad की Dump()विधि है


GetProperty मेथड कहाँ है?
एलन.टॉ।

@ Alen.Toma मुझे var type = typeof(T); var prop = type.GetProperty(propName);संकलन करने के लिए कोड को बदलना पड़ा ।
जाइल्स रॉबर्ट्स

इसे संकलित किया और एक आउटपुट डंप किया
अमित

5

आप DLR पर एक नज़र डाल सकते हैं । यह आपको .NET 2.0 एप्लिकेशन के अंदर स्क्रिप्ट का मूल्यांकन और निष्पादित करने की अनुमति देता है। यहाँ IronRuby के साथ एक नमूना है :

using System;
using IronRuby;
using IronRuby.Runtime;
using Microsoft.Scripting.Hosting;

class App
{
    static void Main()
    {
        var setup = new ScriptRuntimeSetup();
        setup.LanguageSetups.Add(
            new LanguageSetup(
                typeof(RubyContext).AssemblyQualifiedName,
                "IronRuby",
                new[] { "IronRuby" },
                new[] { ".rb" }
            )
        );
        var runtime = new ScriptRuntime(setup);
        var engine = runtime.GetEngine("IronRuby");
        var ec = Ruby.GetExecutionContext(runtime);
        ec.DefineGlobalVariable("bob", new Person
        {
            Name = "Bob",
            Age = 30,
            Weight = 213,
            FavouriteDay = "1/1/2000"
        });
        var eval = engine.Execute<bool>(
            "return ($bob.Age > 3 && $bob.Weight > 50) || $bob.Age < 3"
        );
        Console.WriteLine(eval);

    }
}

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public int Weight { get; set; }
    public string FavouriteDay { get; set; }
}

बेशक यह तकनीक रनटाइम मूल्यांकन पर आधारित है और संकलन समय पर कोड को सत्यापित नहीं किया जा सकता है।


1
मैं 'खराब कोड' के निष्पादन से रक्षा करने में सक्षम होना चाहता हूं ... क्या यह अच्छा होगा?
18

'खराब कोड' से आपका क्या तात्पर्य है? कोई ऐसा व्यक्ति जो अभिव्यक्ति को टाइप कर रहा हो, जो मान्य नहीं है? इस मामले में आपको स्क्रिप्ट का मूल्यांकन करने की कोशिश करते समय एक रनटाइम अपवाद मिलेगा।
डारिन दिमित्रोव

@darin, प्रक्रियाएं शुरू करने, डेटा बदलने आदि जैसी चीजें
9'09 पर sisve

2
'बैड कोड' = वह चीज़ जो किसी प्रकार की फंक <अभिव्यक्ति नहीं है, व्यक्ति, बूल> (जैसे एक डिस्क से फ़ाइलों को हटाना, एक प्रक्रिया को स्पिन करना आदि ...)
कोडब्रेन

1

यहां अंकगणितीय अभिव्यक्तियों के पार्सिंग और मूल्यांकन के लिए स्काला डीएसएल आधारित पार्सर कॉम्बीनेटर का एक उदाहरण है।

import scala.util.parsing.combinator._
/** 
* @author Nicolae Caralicea
* @version 1.0, 04/01/2013
*/
class Arithm extends JavaTokenParsers {
  def expr: Parser[List[String]] = term ~ rep(addTerm | minusTerm) ^^
    { case termValue ~ repValue => termValue ::: repValue.flatten }

  def addTerm: Parser[List[String]] = "+" ~ term ^^
    { case "+" ~ termValue => termValue ::: List("+") }

  def minusTerm: Parser[List[String]] = "-" ~ term ^^
    { case "-" ~ termValue => termValue ::: List("-") }

  def term: Parser[List[String]] = factor ~ rep(multiplyFactor | divideFactor) ^^
    {
      case factorValue1 ~ repfactor => factorValue1 ::: repfactor.flatten
    }

  def multiplyFactor: Parser[List[String]] = "*" ~ factor ^^
    { case "*" ~ factorValue => factorValue ::: List("*") }

  def divideFactor: Parser[List[String]] = "/" ~ factor ^^
    { case "/" ~ factorValue => factorValue ::: List("/") }

  def factor: Parser[List[String]] = floatingPointConstant | parantExpr

  def floatingPointConstant: Parser[List[String]] = floatingPointNumber ^^
    {
      case value => List[String](value)
    }

  def parantExpr: Parser[List[String]] = "(" ~ expr ~ ")" ^^
    {
      case "(" ~ exprValue ~ ")" => exprValue
    }

  def evaluateExpr(expression: String): Double = {
    val parseRes = parseAll(expr, expression)
    if (parseRes.successful) evaluatePostfix(parseRes.get)
    else throw new RuntimeException(parseRes.toString())
  }
  private def evaluatePostfix(postfixExpressionList: List[String]): Double = {
    import scala.collection.immutable.Stack

    def multiply(a: Double, b: Double) = a * b
    def divide(a: Double, b: Double) = a / b
    def add(a: Double, b: Double) = a + b
    def subtract(a: Double, b: Double) = a - b

    def executeOpOnStack(stack: Stack[Any], operation: (Double, Double) => Double): (Stack[Any], Double) = {
      val el1 = stack.top
      val updatedStack1 = stack.pop
      val el2 = updatedStack1.top
      val updatedStack2 = updatedStack1.pop
      val value = operation(el2.toString.toDouble, el1.toString.toDouble)
      (updatedStack2.push(operation(el2.toString.toDouble, el1.toString.toDouble)), value)
    }
    val initial: (Stack[Any], Double) = (Stack(), null.asInstanceOf[Double])
    val res = postfixExpressionList.foldLeft(initial)((computed, item) =>
      item match {
        case "*" => executeOpOnStack(computed._1, multiply)
        case "/" => executeOpOnStack(computed._1, divide)
        case "+" => executeOpOnStack(computed._1, add)
        case "-" => executeOpOnStack(computed._1, subtract)
        case other => (computed._1.push(other), computed._2)
      })
    res._2
  }
}

object TestArithmDSL {
  def main(args: Array[String]): Unit = {
    val arithm = new Arithm
    val actual = arithm.evaluateExpr("(12 + 4 * 6) * ((2 + 3 * ( 4 + 2 ) ) * ( 5 + 12 ))")
    val expected: Double = (12 + 4 * 6) * ((2 + 3 * ( 4 + 2 ) ) * ( 5 + 12 ))
    assert(actual == expected)
  }
}

समतुल्य अभिव्यक्ति का पेड़ या प्रदान की गई अंकगणितीय अभिव्यक्ति का पार्स पेड़ पार्सर [सूची [स्ट्रिंग]] प्रकार का होगा।

अधिक विवरण निम्नलिखित लिंक पर हैं:

http://nicolaecaralicea.blogspot.ca/2013/04/scala-dsl-for-parsing-and-evaluating-of.html


0

डायनामिक लिनेक लाइब्रेरी (जो दृढ़ता से टाइप की गई अभिव्यक्ति का निर्माण करती है और इसके लिए दृढ़ता से टाइप किए गए चर की आवश्यकता होती है) के अलावा, मैं बेहतर विकल्प की सलाह देता हूं: एनआरकेओ कॉमन्स लाइब्रेरी (खुला स्रोत) का वह हिस्सा । यह सभी प्रकारों को संरेखित करता है और रनटाइम पर सभी चालान करता है और गतिशील भाषा की तरह व्यवहार करता है:

var lambdaParser = new NReco.LambdaParser();
var varContext = new Dictionary<string,object>();
varContext["one"] = 1M;
varContext["two"] = "2";

Console.WriteLine( lambdaParser.Eval("two>one && 0<one ? (1+8)/3+1*two : 0", varContext) ); // --> 5

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.