كيفية تحويل سلسلة إلى ما يعادلها LINQ التعبير الشجرة ؟
سؤال
هذا هو نسخة مبسطة من المشكلة الأصلية.
لدي الدرجة يسمى الشخص:
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
وأود أن أغتنم هذه السلسلة و مثيل كائن وتقييم صحيحة أو كاذبة - أيتقييم وظائفها<Person, bool=""> على مثيل الكائن.
هنا هي بلدي الأفكار الحالية:
- تنفيذ القواعد الأساسية في ANTLR لدعم الأساسية المقارنة المنطقية.أنا أفكر في نسخ Visual Basic الأسبقية و بعض featureset هنا: http://msdn.microsoft.com/en-us/library/fw84t893(مقابل.80).aspx
- وقد ANTLR إنشاء مناسبة "أنصار الشريعة" من المقدمة السلسلة.
- المشي AST و استخدام المسند باني إطار حيوي إنشاء ظائفها<Person, bool="">
- تقييم المسند ضد مثيل من الشخص المطلوب
سؤالي هو هل أنا تماما overbaked هذا ؟ أي بدائل ؟
تحرير:اختار الحل
قررت استخدام الديناميكي 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.منطقية, و في هذه الحالة صحيحا.
شكرا جزيلا مارك Gravell.
وتشمل النظام.Linq.ديناميكية nuget حزمة الوثائق هنا
المحلول
هل ديناميكية linq المكتبة المساعدة هنا ؟ على وجه الخصوص, أنا أفكر كما Where
البند.إذا لزم الأمر ، ووضعها داخل قائمة/مجموعة فقط اتصل .Where(string)
على ذلك!أي
var people = new List<Person> { person };
int match = people.Where(filter).Any();
إن لم يكن, الكتابة محلل (باستخدام Expression
تحت غطاء محرك السيارة) ليس كبير فرض ضرائب - كتبت واحدة مماثلة (على الرغم من أنني لا أعتقد أن لدي مصدر) في القطار يوميا قبل الميلاد...
نصائح أخرى
آخر هذه المكتبة الفرار
قمت بعمل مقارنة سريعة ديناميكية Linq المكتبة و الفرار و الفرار من 10 مرات أسرع من أجل التعبير "(Name == \"Johan\" AND Salary > 500) OR (Name != \"Johan\" AND Salary > 300)"
هذا كيف يمكنك كتابة التعليمات البرمجية باستخدام الفرار.
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();
}
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()
طريقة
قد نلقي نظرة على 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; }
}
بالطبع هذه التقنية تعتمد على وقت التقييم و رمز لا يمكن التحقق منها في وقت الترجمة.
هنا هو مثال على سكالا DSL بناء محلل combinator لتحليل وتقييم من التعبيرات الحسابية.
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)
}
}
أي ما يعادل التعبير شجرة أو تحليل شجرة المخصص الحسابي التعبير سيكون من محلل[List[سلسلة]] نوع.
مزيد من التفاصيل على الرابط التالي:
http://nicolaecaralicea.blogspot.ca/2013/04/scala-dsl-for-parsing-and-evaluating-of.html
بالإضافة إلى ديناميكية Linq مكتبة (الذي يبني كتبته بقوة التعبير يتطلب بقوة كتبته المتغيرات) أوصي أفضل بديل:linq محلل ذلك الجزء من NReco العموم المكتبة (مفتوحة المصدر).انها تؤيد جميع أنواع ينفذ كل الدعاء في وقت التشغيل و يتصرف مثل لغة ديناميكية:
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