これをテストすることはできませんでしたが、うまくいくはずです。最後に平方根はありませんが、順序はどちらでも同じでなければなりません。
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);
}
編集:
を使用できないためExpression.Invoke()
、 に渡される式の本体をインライン化する必要がありますEuclideanDistanceOrder
。これを行うための「良い」方法はないように思われるので、それをReplace
行う方法を書きました。私はReplace
いくつかのより一般的なExpression
型に対してのみ実装しました。うまくいけば、これはあなたの使用法をカバーするのに十分でしょうが、他のExpression
型のためにそれを実装する必要があるかもしれません.
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.Constant((double)0);
var agg = expressions.Aggregate((Expression)seed, (s, item) => Expression.Add(s, Expression.Power(Replace(item.Body, item.Parameters[0], parameter), Expression.Constant((double)2))));
return query.OrderBy(Expression.Lambda<Func<T, double>>(agg, parameter));
}
private static Expression Replace(Expression expression, ParameterExpression original, ParameterExpression replacement)
{
if (expression is BinaryExpression)
{
var binaryExpression = (BinaryExpression)expression;
return Expression.MakeBinary(expression.NodeType, Replace(binaryExpression.Left, original, replacement), Replace(binaryExpression.Right, original, replacement), binaryExpression.IsLiftedToNull, binaryExpression.Method, binaryExpression.Conversion);
}
if (expression is ConditionalExpression)
{
var conditionalExpression = (ConditionalExpression)expression;
return Expression.Condition(Replace(conditionalExpression.Test, original, replacement), Replace(conditionalExpression.IfTrue, original, replacement), Replace(conditionalExpression.IfFalse, original, replacement), conditionalExpression.Type);
}
if (expression is ConstantExpression)
{
return expression;
}
if (expression is MemberExpression)
{
var memberExpression = (MemberExpression)expression;
return Expression.MakeMemberAccess(Replace(memberExpression.Expression, original, replacement), memberExpression.Member);
}
if (expression is ParameterExpression)
{
var parameterExpression = (ParameterExpression)expression;
return parameterExpression == original ? replacement : parameterExpression;
}
if (expression is UnaryExpression)
{
var unaryExpression = (UnaryExpression)expression;
return Expression.MakeUnary(unaryExpression.NodeType, Replace(unaryExpression.Operand, original, replacement), unaryExpression.Type, unaryExpression.Method);
}
throw new Exception(string.Format("Unsupported expression type: {0}", expression.NodeType));
}
たとえば、入力式が次の場合:
p => p.X1 - p.X2
p => p.Y1 - p.Y2
元の実装では次のように構築されていました。
i => 0 + expressions[0](i) ^ 2 + expressions[1](i) ^ 2
新しい実装は元の式を取り、入力パラメーター (p
上記) を最終ラムダに渡されるパラメーター ( ) に置き換え、i
式の本体を出力で直接使用します。
i => 0 + (i.X1 - i.X2) ^ 2 + (i.Y1 - i.Y2) ^ 2