Can I cache partially-executed LINQ queries?

Sure, that's totally doable:

public static Func<A, double> ThrowIfNegative<A, double>(this Func<A, double> f)
{
    return a=>
    { 
      double r = f(a);  
      // if r is NaN then this will throw.
      if ( !(r >= 0.0) )
        throw new Exception(); 
      return r;
    };
}

public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
    var d = new Dictionary<A, R>();
    return a=>
    {
        R r;
        if (!d.TryGetValue(a, out r))
        {
          r = f(a);
          d.Add(a, r);
        }
        return r;
    };
}

And now...

Func<T, double> weight = whatever;
weight = weight.ThrowIfNegative().Memoize();

and you're done.


One way is to move the exception into the weight function, or at least simulate doing so, by doing something like:

Func<T, double> weightWithCheck = i =>
    {
        double result = weight(i);
        if (result < 0)
        {
            throw new ArgumentException("Item weights cannot be less than zero.");
        }
        return result;
    };

IEnumerable<KeyValuePair<T, double>> items =
    sequence.Select(item => new KeyValuePair<T, double>(item, weightWithCheck(item)));

double sum = items.Sum(pair => pair.Value);

By this point, if there is an exception to be had, you should have it. You do have to enumerate items before you can be assured of getting the exception, though, but once you get it, you will not call weight again.

Tags:

C#

Linq