4

I am trying to process 2-dimensional arrays with a dynamic mathematical operator.

This is the method in question:

public float[,] Calculate(int[,] a, int[,] b, Func<int,int,float> compute)
{
   int columns = a.GetLength(0);
   int rows = a.GetLength(1);
   float[,] result = new float[columns,rows];

   for(int i = 0; i < columns; i++)
   {
       for(int j = 0; j < rows; j++)
       {
            result[i,j] = compute(a[i,j], b[i,j]);      
       }
   }
   return result;
}

This method is called like this:

int[,] a = new int[2,3] { {2, 6, 19}, {3, -4, 25}};
int[,] b = new int[2,3] { {12, -2, 11}, {1, -11, 0}};

//1
float[,] c = Calculate(a, b, (x,y) => (x+y));
//2
float[,] c = Calculate(a, b, (x,y) => (x-y));
//3
float[,] c = Calculate(a, b, (x,y) => (x*y));
//4
float[,] c = Calculate(a, b, (x,y) => (x/y));

Now, while the version 1 to 3 work flawlessly, version 4 is bound to throw an exception sooner or later. Now one way to handle this is use a try catch block:

try 
{
    result[i,j] = compute(a[i,j], b[i,j]);      
}
catch(DivideByZeroException ex)
{
    result[i,j] = 0;
}

This looks like I use try catch for flow-control and I'd rather avoid it (Also, the result is wrong). Is there a way to check for the division operator in the function call and if the operator is /, check if the second array contains 0 and just return null?

Marco
  • 22,856
  • 9
  • 75
  • 124
  • 2
    Have in mind that this will result in integer division so 5/2 equals 2 not 2.5. You will need to cast at least one of the parameters to float to get real number (float). – Nikola Davidovic Jul 02 '14 at 09:33
  • 1
    Just let `Calculate` throw the exception and handle it in the caller (or the caller's caller). – Henrik Jul 02 '14 at 09:38
  • By "just return null" do you mean that you intend to change your function signature to return a `float?[,]` instead now? And if so, can we change the definition of `compute` to be ``. And if so, can we not allow `compute` to decide whether its arguments make sense or not and decide when to return `null`? – Damien_The_Unbeliever Jul 02 '14 at 10:03
  • @Damien_The_Unbeliever Just Yes - you can/may. – Marco Jul 02 '14 at 10:33

2 Answers2

2

What about usual flow control? An if statement?

float[,] c;

if (b != 0)
    c = Calculate(a, b, (x,y) => (x/y));
else
    c = 0;
Moslem Ben Dhaou
  • 6,897
  • 8
  • 62
  • 93
1

Well this may be look like a little bit strange solution but, you can test your function with a random numbers and check the result, if result is always 1 then it means it's a division operation:

static bool IsDivision(Func<int, int, float> func)
{
    var rnd = new Random();

     return  Enumerable.Range(0, 10)
            .Select(x => rnd.Next(100))
            .All(x => Math.Abs(func(x, x) - 1) < 0.000001);
}

Then before you do your operations, you can pass your func to this method and perform appropriate action:

if(IsDivision(compute) &&  b.OfType<int>().Any(x => x == 0)) return null;

But I agree this is not a great solution, for example this will not work with (x,y) => 1, but I assume you know what you are doing and you will not pass that kind of weird expressions to your function :)

Selman Genç
  • 100,147
  • 13
  • 119
  • 184