1

I have this code, which is supposed to return a value type, applying at each step the transformations specified in steps.

private static T Transformed<T>(T x, params Func<T, T>[] steps) where T : struct
{
     if ((steps?.Length ?? 0) == 0)
     {
        return x;
     }

     var reallyEmpty = steps.Where(f => (x = f(x)).Equals(int.MinValue));
     return x;
}

I only need the Where extension to get through each step without using a loop, thus I use a condition which could probably never be true (Equals(int.MinValue)). But if I have this calling code, I get 5 and not 15, how I'd expect.

int res1 = Transformed(5, x => x * 2, x => x + 5);
Console.WriteLine(res1);

My question is why? Doesn't Where go through each element and check it?

klashar
  • 2,519
  • 2
  • 28
  • 38
NotADeveloper
  • 347
  • 2
  • 5
  • 17
  • 2
    You aren't iterating over `reallyEmpty` so the side-effect `x = f(x)` is never executed. – Lee Feb 21 '17 at 15:36
  • If you are a big fan of linq, you can create [ForEach](http://stackoverflow.com/a/1509458/1997232) extension and never ever again confuse yourself or others. – Sinatr Feb 21 '17 at 15:51

2 Answers2

12

The Where is lazily evaluated - you're never using the result of it, so the predicates are never being evaluated.

You could force iteration by counting the results or similar:

var ignored  steps.Where(f => (x = f(x)).Equals(int.MinValue)).Count();

... but it would be significantly clearer to just loop yourself:

foreach (var step in steps) 
{
    x = step(x);
}

After all, you're not really avoiding a loop by using Where - you're just hiding it, and in doing so you're overcomplicating your code to the extent that you don't understand it any more.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

If you're really set on using LINQ, it's possible to do what you want using Aggregate:

private static T Transformed<T>( T x, params Func<T, T>[] steps ) where T : struct
{
    return steps?.Aggregate( x, ( accum, f ) => f( accum ) ) ?? x;
}

I don't typically find aggregate particularly readable, but I figured it was worth mentioning.

Kyle
  • 6,500
  • 2
  • 31
  • 41