4

In C# how do I memoize a function with two arguments?

Do I have to curry before memoization?

Wes Dyer wrote the Memoization code I typically use, but now I need two arguments

Chris S
  • 64,770
  • 52
  • 221
  • 239
CVertex
  • 17,997
  • 28
  • 94
  • 124

5 Answers5

5

You just make an overloaded version of the Memoize method that has three generic types and takes a function with two parameters, and the two arguments. It still returns a parameterless function:

public static Func<R> Memoize<A1,A2,R>(this Func<A1,A2,R> f, A1 a1, A2 a2)
{
  R value = default(R);
  bool hasValue = false;
  return () =>
    {
      if (!hasValue)
      {
        hasValue = true;
        value = f(a1,a2);
      }
      return value;
    };
}

Edit:
Alternatively, you need to make a custom IEqualityComparer for a KeyValuePair that contains the two arguments, for the Memoize method to be able to return a function with two parameters:

public static Func<A1,A2,R> Memoize<A1,A2,R>(this Func<A1,A2,R> f, IEqualityComparer<KeyValuePair<A1,A2>> comparer)
{
   var map = new Dictionary<KeyValuePair<A1,A2>,R>(comparer);
   return (a1,a2) =>
      {
         R value;
         KeyValuePair<A1,A2> key = new KeyValuePair<A1,A2>(a1,a2);
         if (map.TryGetValue(key, out value)) {
            return value;
         }
         value = f(a1,a2);
         map.Add(key, value);
         return value;
      };
}
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
5

Wes has another post where he gives a two (or more) argument version of Memoize. It does not require a custom comparer.

Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
  • Yes, this code is much better by Wes. You and Guffa are correct, but Guffa put more effort in, so I'm awarding it to him. Thanks though! – CVertex Mar 12 '09 at 00:43
3

With new versions of .NET you can simplify the accepted solution's code a bit by using tuples

    public static Func<TParam1, TParam2, TReturn> Memoize<TParam1, TParam2, TReturn>(Func<TParam1, TParam2, TReturn> func)
    {
        var map = new Dictionary<Tuple<TParam1, TParam2>, TReturn>();
        return (param1, param2) =>
        {
            var key = Tuple.Create(param1, param2);
            TReturn result;
            if (!map.TryGetValue(key, out result))
            {
                result = func(param1, param2);
                map.Add(key, result);
            }
            return result;
        };
    }
telesphore4
  • 877
  • 1
  • 7
  • 19
2

You should be able to memoize a pair. The two arg function calls a single arg function that you memoize.

Miserable Variable
  • 28,432
  • 15
  • 72
  • 133
1

I also did some work on memoization in C#. My results are similar but use a dictionary key derived from the concatenation of the input object hash codes. The pattern can be extended to as many intputs as Func<> will allow.

Se more here: http://bit.ly/t6iNJP