Generische Methode zur Berechnung der euklidischen Entfernung mit Hilfe von Ausdrücken

c# expression expression-trees linq

Frage

Ich möchte eine Methode, die die euklidische Distanz mit Hilfe von Ausdrücken berechnen und ein IQueryable bestellen kann:

sqrt [(q1 - p1) ^ 2 + (q2 - p2) ^ 2 + ... + (qn - pn) ^ 2]

Dies ist die Methodensignatur, die ich mir ausgedacht habe:

public static IOrderedQueryable<T> EuclideanDistanceOrder<T>(
    this IQueryable<T> query, IEnumerable<Expression<Func<T, double>>> expressions)
    {
        var orderedQuery = query.OrderBy(i => Math.Sqrt(expressions.Aggregate((total, item) => total + Math.Pow(item, 2))));
        return orderedQuery;
    }

Ich bin mir nicht sicher, was ich mit item und total (da sie Expression<Func<T, double>> ). Ich habe dies auf verschiedene Arten versucht, einschließlich Expression.Power und Expression.Add . Ich habe versucht, die Ausdrücke getrennt zu definieren:

Expression<Func<double, double>> power = i => Math.Pow(i, 2);
Expression<Func<List<Expression<Func<T, double>>>, double>> dist = (items) => Math.Sqrt(items.Sum(power));

Aber ich weiß immer noch nicht, was ich mit power .

Kann ich Hilfe bekommen? Gibt es einen besseren Weg, um das zu erreichen?

Beliebte Antwort

Um es mit EF oder LinqToSQL arbeiten zu können, müßten Sie alle Informationen wie Expressions selbst als Property-Accessoren für P und Q übergeben. Deshalb habe ich Ihre Methodendeklaration geändert:

public static class Extension
{
    public static IOrderedQueryable<T> EuclideanDistanceOrder<T>(
        this IQueryable<T> query, 
        IEnumerable<Expression<Func<T, double>>> pExpressions, 
        IEnumerable<Expression<Func<T, double>>> qExpressions)
    {
        var parameter = Expression.Parameter(typeof(T));
        var pBodies = pExpressions
            .Select(x => ReplaceParameter(x.Body, parameter))
            .ToArray();

        var qBodies = qExpressions
            .Select(x => ReplaceParameter(x.Body, parameter))
            .ToArray();

        var distances = pBodies
            .Select((x, i) => CreateDistance(x, qBodies[i]))
            .ToArray();

        var squers = distances
            .Select(x => CreateSquerExpression(x))
            .ToArray();

        var sum = squers.First();
        for (int i = 1; i < squers.Count(); i++)
        {
            sum = Expression.Add(sum, squers[i]);
        }
        var funcExpression = Expression.Lambda<Func<T, double>>(sum, parameter);
        //the sqrt is irrelevant to order of this sequence
        return query.OrderBy(funcExpression);
    }

    private static Expression CreateDistance(Expression p, Expression q)
    {
        return Expression.Subtract(q, p);
    }

    private static Expression CreateSquerExpression(Expression x)
    {
        var method = typeof(Math).GetMethod("Pow", BindingFlags.Static | BindingFlags.Public);
        return Expression.Call(method, x, Expression.Constant(2.0));
    }

    private static Expression ReplaceParameter(Expression expression, ParameterExpression parameter)
    {
        var unaryExpression = expression as UnaryExpression;
        MemberExpression memberExpression;
        if (unaryExpression != null)
        {
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = expression as MemberExpression;
        }

        if (memberExpression == null)
            throw new NotImplementedException();

        if (!(memberExpression.Expression is ParameterExpression) || !(memberExpression.Member is PropertyInfo))
            throw new NotImplementedException();

        return Expression.Property(parameter, (PropertyInfo)memberExpression.Member);
    }
}

Beim Aufruf wie folgt:

    var list = new[]{ new Item
    {
        P1 = 0,
        Q1 = 0,
        P2 = 3,
        Q2 = 1,
    },
    new Item
    {
        P1 = 0,
        Q1 = 0,
        P2 = 2,
        Q2 = 1,
    }
};

var query = list.AsQueryable();

var result = query.EuclideanDistanceOrder(new Expression<Func<Item, double>>[]{
    x => x.P1,
    x => x.P2
},
new Expression<Func<Item, double>>[]{
    x => x.Q1,
    x => x.Q2
}).ToArray();

internal class Item
{
    public double P1 { get; set; }
    public double Q1 { get; set; }
    public double P2 { get; set; }
    public double Q2 { get; set; }
}

Es funktioniert für liq zu Objekten. Ich bin nur nicht sicher, ob EF oder linqtoSql Math.Power Methode zu SQL zuordnen wird. Wenn nicht, ist es einfach genug, zur Multiplikation zu wechseln.



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum