kastrbl4nik
kastrbl4nik

Reputation: 97

How to get the array length of an expression parameter

This method should return a function which can evaluate the scalar product of two vectors of the same size:

public static Func<T[], T[], T> GetVectorMultiplyFunction<T>() where T : struct
{
    var x = Expression.Parameter(typeof(T[]), "x");
    var y = Expression.Parameter(typeof(T[]), "y");


    var body = Enumerable
        .Range(0, 3 /*Expression.ArrayLength(x)*/)
        .Select(i => Expression.Multiply(
            Expression.ArrayIndex(x, Expression.Constant(i)),
            Expression.ArrayIndex(y, Expression.Constant(i))
        ))
        .Aggregate(Expression.Add);

    var lambda = Expression.Lambda<Func<T[], T[], T>>(body, x, y);

    return lambda.Compile();
} 

But in order to do that, I need to know the length of an array. There's a method Expression.ArrayLength() which returns UnaryExpression instead of the int and the expression cannot be cast explicitly to int. Is there a way of evaluating the array length as int beforehand?

UPD

Here's a unit test showing how this method should work:

[Test]
public void GetVectorMultiplyFunctionReturnsFunctionForInt()
{
    var first = new int[] { 1, 2, 3 };
    var second = new int[] { 2, 2, 2 };
    var expected = 1 * 2 + 2 * 2 + 3 * 2;
    var func = GetVectorMultiplyFunction<int>();
    var actual = func(first, second);
    Assert.AreEqual(expected, actual);
}

Upvotes: 1

Views: 180

Answers (3)

Simon MᶜKenzie
Simon MᶜKenzie

Reputation: 8694

The only parts of your code that require expression trees (assuming you can't use generic math or dynamics as per the other two solutions) are the operators (add and multiply).

If you compile the operator expressions separately, you can then use LINQ very easily, as per your original approach - something like this:

public static Func<T[], T[], T> GetVectorMultiplyFunction<T>() where T : struct
{
    static Func<T,T,T> GetOperatorFunction(Func<Expression, Expression, BinaryExpression> operatorExpression)
    {
        var x = Expression.Parameter(typeof(T));
        var y = Expression.Parameter(typeof(T));
        var body = operatorExpression(x, y);

        return Expression
            .Lambda<Func<T, T, T>>(body, x, y)
            .Compile();
    }

    var add = GetOperatorFunction(Expression.Add);
    var multiply = GetOperatorFunction(Expression.Multiply);

    return (xVector, yVector) =>
        xVector
            .Zip(yVector, multiply)
            .Aggregate(add);
}

Upvotes: 1

Jeremy Lakeman
Jeremy Lakeman

Reputation: 11173

The length of an array is not part of the type definition, it's a value you can only obtain at runtime.

With the recent introduction of Generic Math, you can multiply and add values using generics without needing to build an expression tree;

public static T Calc<T>(T[] x, T[] y)
    where T : struct, IMultiplyOperators<T,T,T>, IAdditionOperators<T,T,T>
{
     T result = default(T);

     for(var i = 0; i < x.Length && i < y.Length; i++)
         result += x[i] * y[i];

     return result;
}

But I assume that for your assignment you are expected to build an expression tree equivalent to the above method, then compile it.

Upvotes: 3

SBFrancies
SBFrancies

Reputation: 4250

This may not be what you want but seems to solve your problem if all you need to retain is the signature:

public static Func<T[], T[], T> GetVectorMultiplyFunction<T>() where T : struct
{
     return (T[] x, T[] y) => {
     T result = default(T);      

     for(var i = 0; i < x.Length; i++)
     {
         var xd = (dynamic)x[i];
         var yd = (dynamic)y[i];
         result += (xd * yd);
     }
     
     return result;
};

Upvotes: 2

Related Questions