Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用表达式计算欧氏距离的通用方法_C#_Linq_Expression_Expression Trees - Fatal编程技术网

C# 用表达式计算欧氏距离的通用方法

C# 用表达式计算欧氏距离的通用方法,c#,linq,expression,expression-trees,C#,Linq,Expression,Expression Trees,我想要一种方法,可以使用表达式计算欧几里德距离,并订购IQueryable: sqrt[(q1-p1)^2+(q2-p2)^2+…+(qn-pn)^2] 这是我想出的方法签名: public static IOrderedQueryable<T> EuclideanDistanceOrder<T>( this IQueryable<T> query, IEnumerable<Expression<Func<T, double>&

我想要一种方法,可以使用表达式计算欧几里德距离,并订购IQueryable:

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

这是我想出的方法签名:

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;
    }
但我仍然不知道如何使用
电源


有没有更好的方法来解决这个问题?

我还没能测试这个,但它似乎应该能工作。末尾没有平方根,但顺序应该是相同的

public static IOrderedQueryable<T> EuclideanDistanceOrder<T>(this IQueryable<T> query, IEnumerable<Expression<Func<T, double>>> expressions)
{
    var parameter = Expression.Parameter(typeof(T), "item");
    var seed = Expression.Lambda<Func<T, double>>(Expression.Constant((double)0), parameter);
    return query.OrderBy(expressions.Aggregate(seed, GetAggregateExpression));
}

private static Expression<Func<T, double>> GetAggregateExpression<T>(Expression<Func<T, double>> sum, Expression<Func<T, double>> item)
{
    var parameter = Expression.Parameter(typeof(T), "item");
    return Expression.Lambda<Func<T, double>>(Expression.Add(Expression.Invoke(sum, parameter), Expression.Power(Expression.Invoke(item, parameter), Expression.Constant((double)2))), parameter);
}
例如,如果我们的输入表达式是:

p => p.X1 - p.X2
p => p.Y1 - p.Y2
最初的实现将构建:

i => 0 + expressions[0](i) ^ 2 + expressions[1](i) ^ 2
新实现采用原始表达式,并将输入参数(
p
)替换为将传递给最终lambda(
i
)的参数,并在输出中直接使用表达式体:

i => 0 + (i.X1 - i.X2) ^ 2 + (i.Y1 - i.Y2) ^ 2

要使其与EF或LinqToSQL一起工作,您必须将所有信息作为表达式传递,甚至是p和Q的属性访问器。因此,我修改了您的方法声明:

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);
    }
}
公共静态类扩展
{
公共静态IOR可查询的欧几里德常数序(
这是一个易懂的问题,
IEnumerable表达式,
IEnumerable qExpressions)
{
var参数=表达式参数(typeof(T));
var pBodies=pExpressions
.Select(x=>replaceparmeter(x.Body,parameter))
.ToArray();
var qbody=qExpressions
.Select(x=>replaceparmeter(x.Body,parameter))
.ToArray();
变量距离=0
.选择((x,i)=>CreateDistance(x,qBody[i]))
.ToArray();
var squers=距离
.选择(x=>CreateSquerePression(x))
.ToArray();
var sum=squers.First();
对于(int i=1;i
这样调用时:

    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; }
}
var list=new[]{new Item
{
P1=0,
Q1=0,
P2=3,
Q2=1,
},
新项目
{
P1=0,
Q1=0,
P2=2,
Q2=1,
}
};
var query=list.AsQueryable();
var result=query.euclideandstanceorder(新表达式[]){
x=>x.P1,
x=>x.P2
},
新表达式[]{
x=>x.Q1,
x=>x.Q2
}).ToArray();
内部类项目
{
公共双P1{get;set;}
公共双Q1{get;set;}
公共双P2{get;set;}
公共双Q2{get;set;}
}

它适用于液体到物体。我只是不确定EF或linqtoSql是否会将
Math.Power
方法映射到sql。如果不是的话,就很容易改成乘法

我应该补充一点,这需要能够在linq to sql和实体框架中执行。@AndreCalil现在我再看一遍,是的,它看起来确实有点像家庭作业,但这只是我自己的一个爱好项目…@AndreCalil如果是家庭作业会发生什么?即使是家庭作业,他也努力寻找解决方案。你想知道pow函数在做什么吗?
数学。pow(I,2)
可以替换为
I*I
;小错误是常量应该是double而不是int。然后我得到了ArgumentExecution“至少有一个对象必须实现IComparable。”感谢您的建议。不幸的是,Linq不支持Expression.Invoke到Entities。@cofiem我编辑了答案以提供无调用的实现。看看这对你有什么好处。@Iridium非常感谢你的帮助。我已经尝试过了,确实有一些表达式需要实现。第一个抛出异常的是
节点类型
=
调用
。我在想,如果没有大量的工作,我想要做的可能是不可能的。也许我应该考虑一种不太通用的方法来实现这一点?如果你有许多不同的表达式集要用于欧几里德排序,或者你要将其用于许多不同类型的“T”,那么使用通用路径可能会很有用。这是你需要回答的问题。如果你决定继续这条路线,如果你需要的话,为呼叫添加一个替换处理程序应该很容易。非常感谢。当我运行这个函数时,我得到类型为'System.Func`2[System.Double]'的表达式不能用于方法'Double Pow(Double,Double)'的类型'System.Double'的参数。我正在linq to对象上执行它,它工作正常。我不明白为什么它会扔给你。作为Pow函数的参数,我传递类型为
SimpleBinaryExpression
的距离和其他参数
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);
    }
}
    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; }
}