Consider two expression trees:
Expression<Func<float, float, float>> f1 = (x, y) => x + y;
Expression<Func<float, float>> f2 = x => x * x;
I want to substitute the expression f2 as a second parameter of f1 and obtain the following expression:
Expression<Func<float, float, float>> f3 = (x, y) => x + y * y;
The simplest way is to use Expression.Lambda and Expression.Invoke, but the result will look like
(x, y) => f1(x, f2(y))
But this is unacceptable for me due to ORM limitations that cannot handle invoke/lambda properly.
Is it possible to construct the expression without full traversal of expression trees? A working example that fulfill my needs can be found here but I want simpler solution.
You cannot do it without full traversal of both expressions. Fortunately, ExpressionVisitor makes full traversal really easy:
class ReplaceParameter : ExpressionVisitor {
private readonly Expression replacement;
private readonly ParameterExpression parameter;
public ReplaceParameter(
ParameterExpression parameter
, Expression replacement
) {
this.replacement = replacement;
this.parameter = parameter;
}
protected override Expression VisitParameter(ParameterExpression node) {
return node == parameter ? replacement : node;
}
}
Use this visitor twice to complete the replacement:
Expression<Func<float,float,float>> f1 = (x, y) => x + y;
Expression<Func<float,float>> f2 = x => x * x;
var pX = f2.Parameters[0];
var pY = f1.Parameters[1];
var replacerF2 = new ReplaceParameter(pX, pY);
var replacerF1 = new ReplaceParameter(pY, replacerF2.Visit(f2.Body));
var modifiedF1 = Expression.Lambda(
replacerF1.Visit(f1.Body)
, f1.Parameters
);
Console.WriteLine(modifiedF1);
The above prints
(x, y) => (x + (y * y))
Demo.
Related
Can we simplify the following with ?? and lambda expression?
Func<int, int> func = f; // f is a function parameter
if (func == null) // if f passed by the user is null then we use the default identity function f(x)=x.
func = x => x;
I cannot do something Func<int, int> func = f ?? x=>x;. Could you?
Edit
My scenario is as follows.
class Program
{
static double Average(int[] data, Func<int, int> f = null)
{
int sum = 0;
Func<int, int> func = f ?? new Func<int, int>(x => x);
//Func<int, int> func = f;
//if (func == null)
// func = x => x;
foreach (int x in data)
sum += func(x);
return (double)sum / data.Length;
}
static void Main(string[] args)
{
int[] data = { 1, 2, 3 };
Console.WriteLine(Average(data));
}
}
The precedence of null-coalescing operator is higher than lambda declaration.
Func<int, int> func = f ?? (x => x);
Possible duplicate of Null-coalescing operator and lambda expression
This should work
Func<int, int> func = f ?? (x=>x);
Your statement is parsed as
Func<int, int> func = (f ?? x)=>x;
Please search through the available answers first to check if somebody has already answered it.
I have my custom Visitor which looks to right and left and changes parameters to constant.
I know that just change node is not possible.
I should return new lambda expression which contains constants instead parameters.
But I can not create an expression myself :(
I have this code:
public class ParametersTransformToConstantVisitor : ExpressionVisitor
{
private Dictionary<string, ConstantExpression> parameters = new Dictionary<string, ConstantExpression>();
public ParametersTransformToConstantVisitor(Dictionary<string, ConstantExpression> parameters)
{
this.parameters = parameters;
}
protected override Expression VisitBinary(BinaryExpression node)
{
var constExprLeftName = new Lazy<string>(() => ((ParameterExpression) node.Left)?.Name);
var constExprRightName = new Lazy<string>(() => ((ParameterExpression) node.Right)?.Name);
var constExprName = new Lazy<string>(() => ((ParameterExpression) node.Reduce())?.Name);
ParameterExpression leftParam = null;
ParameterExpression rightParam = null;
if (node.NodeType == ExpressionType.Parameter && parameters.ContainsKey(constExprName.Value))
{
return parameters[constExprName.Value];
}
if (node.Left.NodeType == ExpressionType.Parameter && parameters.ContainsKey(constExprLeftName.Value))
{
leftParam = (ParameterExpression) node.Left;
}
if (node.Right.NodeType == ExpressionType.Parameter && parameters.ContainsKey(constExprLeftName.Value))
{
rightParam = (ParameterExpression) node.Right;
}
if (leftParam != null || rightParam != null)
{
//return Expression.Lambda();
}
return base.VisitBinary(node);
}
}
Help me to build lambda expression, please
It feels like all you actually need here is:
protected override Expression VisitParameter(ParameterExpression node)
=> parameters.TryGetValue(node.Name, out var ce) ? (Expression)ce : node;
protected override Expression VisitLambda<T>(Expression<T> node)
=> Expression.Lambda(Visit(node.Body), node.Parameters); // don't visit the parameters
i.e. whenever the visitor sees a ParameterExpression, if there is a corresponding item in the parameters map, use that value.
The override on VisitLambda is because VisitLambda still needs to return a lambda of the same shape, and the default implementation would also visit (and thus swap) out the parameters from the declaration.
It is the visitor's job to worry about reassembling the tree around your changes.
Note, however, that if you are trying to create a parameterless lambda, you might also need to rewrite the root. Or you could just use the .Body and forget about the parameters.
Example:
Expression<Func<int, int, string>> add = (x, y) => ((2 * x) + y).ToString();
Console.WriteLine(add);
var args = new Dictionary<string, ConstantExpression>
{
["x"] = Expression.Constant(4),
["y"] = Expression.Constant(1),
};
var visitor = new ParametersTransformToConstantVisitor(args);
var result = (LambdaExpression)visitor.Visit(add);
Console.WriteLine(result);
which gives:
(x, y) => ((2 * x) + y).ToString()
(x, y) => ((2 * 4) + 1).ToString()
You can make this into a parameterless lambda via:
var withoutArgs = Expression.Lambda<Func<string>>(result.Body);
Console.WriteLine(withoutArgs);
which gives:
() => ((2 * 4) + 1).ToString()
minor addition: you might also want to simplify in the visitor:
protected override Expression VisitBinary(BinaryExpression node)
{
var visited = base.VisitBinary(node);
if(visited is BinaryExpression be
&& be.Method == null && be.Conversion == null
&& !be.IsLifted
&& be.Left is ConstantExpression left
&& be.Right is ConstantExpression right)
{
object val;
switch(be.NodeType)
{
case ExpressionType.Add:
val = (dynamic)left.Value + (dynamic)right.Value;
break;
case ExpressionType.Multiply:
val = (dynamic)left.Value * (dynamic)right.Value;
break;
case ExpressionType.Subtract:
val = (dynamic)left.Value - (dynamic)right.Value;
break;
case ExpressionType.Divide:
val = (dynamic)left.Value / (dynamic)right.Value;
break;
default:
return visited; // unknown
}
return Expression.Constant(
Convert.ChangeType(val, visited.Type), visited.Type);
}
return visited;
}
This changes the outputs to:
(x, y) => ((2 * x) + y).ToString()
(x, y) => 9.ToString()
() => 9.ToString()
and we could possibly also even hoist the ToString()!
protected override Expression VisitMethodCall(MethodCallExpression node)
{
var visited = base.VisitMethodCall(node);
if (visited is MethodCallExpression mce)
{
if ((mce.Object == null || mce.Object is ConstantExpression)
&& mce.Arguments.All(x => x is ConstantExpression))
{
var obj = (mce.Object as ConstantExpression)?.Value;
var args = mce.Arguments.Select(x => ((ConstantExpression)x).Value).ToArray();
var result = mce.Method.Invoke(obj, args);
return Expression.Constant(result, mce.Type);
}
}
return visited;
}
which now gives us:
(x, y) => ((2 * x) + y).ToString()
(x, y) => "9"
() => "9"
Can I write this
return (a, b) => (b == 0) ? 0: a / b;
With an if-else to the right of (a, b) =>
If you are returning a Func<double, double, double> it could be re-written as
return (a, b) =>
{
if(b == 0)
return 0;
else
return a / b;
};
If you are returning a Expression<Func<double, double, double>> it is not very easy to do, you will need to create a custom Expression by hand to represent the statement as there is no way to represent it in C#.
Have not tested it, but I think the Expression version would be
public Expression<Func<double, double, double>> IThinkThisWorks()
{
var paramA = Expression.Parameter(typeof(double), "a");
var paramB = Expression.Parameter(typeof(double), "b");
var const0 = Expression.Constant(0.0);
var test = Expression.Equal(paramA, paramB);
var division = Expression.Divide(paramA, paramB);
var ifCheck = Expression.IfThenElse(test, const0, division);
return Expression.Lambda<Func<double, double, double>>(ifCheck, paramA, paramB);
}
I've decided to take some golden time to learn a bit more about Expressions. I'm trying a very simple exercise, namely adding two numbers. I've hit an exception that's proving to be tricky to search for.
Here's my code
Expression<Func<int,int,int>> addExpr = (x, y) => x + y;
var p1 = Expression.Parameter(typeof(int), "p1");
var p2 = Expression.Parameter(typeof(int), "p2");
var lambda = Expression.Lambda<Func<int,int,int>>(addExpr, p1, p2); //<-here
var del = lambda.Compile();
var result = del(2,3); //expect 5
but this is throwing an ArgumentException : Expression of type 'System.Func`3[System.Int32,System.Int32,System.Int32]' cannot be used for return type 'System.Int32'
at the line indicated above. What have I done wrong?
You need to wrap the addExpr in an invoke using the expression parameters
Expression<Func<int,int,int>> addExpr = (x, y) => x + y;
var p1 = Expression.Parameter(typeof(int), "p1");
var p2 = Expression.Parameter(typeof(int), "p2");
var invokeExpression=Expression.Invoke(addExpr,p1,p2);
var lambda = Expression.Lambda<Func<int,int,int>>(invokeExpression,p1,p2);
var del = lambda.Compile();
var result=del(2,3);
The invoke is how you type p1 to x and p2 to y, alternatively you could just write the above as
var p1 = Expression.Parameter(typeof(int), "p1");
var p2 = Expression.Parameter(typeof(int), "p2");
var lambda=Expresion.Lambda<Func<int,int,int>>(Expression.Add(p1,p2),p1,p2);
var del = lambda.Compile();
var result=del(2,3);
Otherwise you need to grab the expression body into the lambda and pass the expression parameters.
var lambda=Expresion.Lambda<Func<int,int,int>>(addExpr.Body,addExpr.Parameters);
Your code should be:
var lambda = Expression.Lambda<Func<Expression<Func<int, int, int>>, int, int>(addExpr, p1, p2);
Your current code expects an int and your passing in Expression<Func<int, int, int>>.
Update
Actually the above won't compile, you would need to do:
var lambda = Expression.Lambda<Func<int, int, int>>(Expression.Add(p1, p2), p1, p2);
You need to decompose addExpr's body or preferably just write it from scratch ie Expression.Add(p1,p2) instead of addExpr.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Nested “from” LINQ query expressed with extension methods
I'm sure this has been asked before, but I honestly couldn't find anything.
I'm curious what the equivalent syntax would be for the following using only built-in Linq extension methods:
var z1 =
from x in xs
from y in ys
select new { x, y };
I can get the same results with this:
var z2 = xs.SelectMany(x => ys.Select(y => new { x, y }));
But it produces different IL code, and the code is a bit convoluted and hard to understand. Is there a cleaner way to do this with extension methods?
Here's my entire test method as written:
private void Test()
{
var xs = new[] { 1D, 2D, 3D };
var ys = new[] { 4D, 5D, 6D };
var z1 =
from x in xs
from y in ys
select new { x, y };
var z2 = xs.SelectMany(x => ys.Select(y => new { x, y }));
}
Here's the [Edit: C# interp of the] IL code (using ILSpy):
private void Test()
{
double[] xs = new double[]
{
1.0,
2.0,
3.0
};
double[] ys = new double[]
{
4.0,
5.0,
6.0
};
var z =
from x in xs
from y in ys
select new
{
x = x,
y = y
};
var z2 = xs.SelectMany((double x) =>
from y in ys
select new
{
x = x,
y = y
});
}
One way would be:
var z2 = xs.SelectMany(x => ys, (x, y) => new {x, y});
If you really want to use a single LINQ extension method, then another candidate would be Join, with the outerKeySelector and innerKeySelector functions defined such that they will always produce equal values.
var z3 = xs.Join(ys, x => true, y => true, (x, y) => new { x, y });
This will, however, probably give more convoluted IL code than the nested from solution. Incidentally, MSDN uses the nested from in its example for a cross join; look at the first code snippet in How to: Perform Custom Join Operations (C# Programming Guide).