NotADeveloper
NotADeveloper

Reputation: 347

Unexpected result with Where()

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?

Upvotes: 1

Views: 227

Answers (2)

Kyle
Kyle

Reputation: 6684

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.

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1499760

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.

Upvotes: 12

Related Questions