Jedi_Maseter_Sam
Jedi_Maseter_Sam

Reputation: 813

How to programmatically convert a loop to a single expression?

I have been reading a book about mathematics for linear algebra and calculus for programming, and one thing that has stuck out to me is the verbosity of the code. First of all, I know what it is that way, the performance is much better than using a for-loop, but, this makes the code extremely hard to read.

Is there a way to use a for-loop to create the verbose function, that way, the code is more maintainable?

Here is a very simple example of what I am talking about:

public int Sum(int[] a, int[] b, int size)
{
   var sum = 0;

   for(var index = 0; index < size; index++)
   {
      sum += a[index] + b[index];
   }

   return sum;
}

Now let's say size is 3. Is it possible to produce a single expression from the method Sum that looks like:

public int Sum3(int[] a, int[] b)
{
   return a[0] + b [0] + a[1] + b[1] + a[2] + b[2];
}

So, in actuality, the method Sum is not execute to do the summation, instead, it is used to create a delegate which can do the summation. It would end up looking like Func<int[], int[], int> Sum3.

The goal would be to use this approach to do operations like the dot product, determinant and matrix multiplication.

UPDATE:

I am NOT looking to sum two arrays in one line of code. That was simply an example. I want to be able to do any vector or matrix operation without actually looping.

Upvotes: 2

Views: 294

Answers (3)

Zev Spitz
Zev Spitz

Reputation: 15357

(This is only a partial answer; it doesn't deal with reading the IL of the Sum metbod.)

Once you've parsed the original function (Sum in this case), you can construct the corresponding Sum3 function as follows:

// using static System.Linq.Expressions.Expression;

var a = Parameter(typeof(int[]));
var b = Parameter(typeof(int[]));
var expr1 = Lambda(
    Add(
        Add(
            Add(
                Add(
                    Add(
                        ArrayIndex(a, Constant(0)),
                        ArrayIndex(b, Constant(0))
                    ),
                    ArrayIndex(a,Constant(1))
                ),
                ArrayIndex(b, Constant(1))
            ),
            ArrayIndex(a, Constant(2))
        ),
        ArrayIndex(b, Constant(2))
    ),
    a,
    b
);
var fn = expr1.Compile();
fn.DynamicInvoke(new[] { 1, 2, 3 }, new[] { 4, 5, 6 });

Of course, you will probably want to customize the logic based on what is in the IL.

Also note that this may well be less performant, because the time it takes to compile expression trees is usually at least an order of magnitude greater than simply looping. Unless you can cache the compiled delegates for various values of size, and the number of usages justifies this.


NB Most of this code comes from the ExpressionTreeToString library that I've written, which can produce the factory method calls needed to create the given expression tree.

// using ExpressionTreeToString;

Expression<Func<int[], int[], int>> expr = (a, b) => a[0] + b[0] + a[1] + b[1] + a[2] + b[2];
Console.WriteLine(expr.ToString("Factory methods"));

Upvotes: 3

Artur
Artur

Reputation: 342

If you really want no iteration over the data in your final runtime, I do not believe it to be possible to accomplish this during the compile time, unless you always know the exact length of your arrays (or you make Int32.MaxValue overrides for your sum). You'd have to write some script with your C# code during the runtime and then execute it.

Upvotes: 0

StepUp
StepUp

Reputation: 38164

Maybe this way is applicable to you:

int[] arr1 = { 1, 2, 3, 4, 5 };
int[] arr2 = { 1, 2, 3, 4, 5 };

int index = 3;
var overallSum = arr1.Take(index).Sum() + arr2.Take(index).Sum();

Upvotes: 0

Related Questions