I love going to the Michipug meetings. I don’t use python on a day to day basis, but I find that talking to those who do really helps me to think about things differently.
Thursday, even before going into the Michipug meeting, I was reading a python blog or something in anticipation of the meeting that night. Something I read triggered me to think about python’s filter, map, and reduce built-in functions. I wondered what .NET has to correspond to these. Well, List.FindAll and List.ConvertAll and their Array counterparts do the work of filter and map. That left reduce.
I couldn’t find reduce, so I whipped up an implementation. I started with just an integer implementation. Again, Func<T,T,T> is a delegate definition straight out of System.Query in the LINQ preview, or Rhino.Commons or wherever.
public int Reduce( Func<int,int,int> function, List<int> list )
{
IEnumerator<int> enumerator = list.GetEnumerator();
enumerator.MoveNext();
T result = enumerator.Current;
while(enumerator.MoveNext())
{
T item = enumerator.Current;
result = function( result, item );
}
return result;
}
Once I had this running, I just made it generic.
public T Reduce<T>( Func<T,T,T> function, IEnumerable<T> list )
{
IEnumerator<T> enumerator = list.GetEnumerator();
enumerator.MoveNext();
T result = enumerator.Current;
while(enumerator.MoveNext())
{
T item = enumerator.Current;
result = function( result, item );
}
return result;
}
I also changed the List<T> to IEnumerable<T> so that it would work with Arrays, Lists, or anything Enumberable.
So what is the point? Well, I don’t have one, other than “its cool!”
Its nice to write factorial as simply as this:
var res = Reduce( ( x, y )=> x * y, list );
or sum a list like this:
var res = Reduce( ( x, y )=> x + y, list );
Its very strange when string.Join(“, “…) doesn’t use Join
Reduce( (x,y) => string.Format("{0}, {1}",x ,y) , message)
And even stranger when you can reverse the join order
Reduce( (x,y) => string.Format("{1}, {0}",x ,y) , message)
What else can you do with reduce?
Well, I don’t know. Give a google for python reduce and you will see that Guido is even getting rid of filter, map, and reduce from Python in the future. So .NET just got filter and map in the form of Array and List member methods which take delegates as arguments, and python is removing filter and map in favor of its list comprehensions and generator expressions.
So what? Well, I don’t really think it means much. In the same article Guido talks about adding two functions, Any and All. .NET already has these in the form of Exists and TrueForAll as Array and List members.
// python: any(x > 42 for x in S) # True if any elements of S are > 42
S.Exists( x => x > 42 );
// python: all(x != 0 for x in S) # True if all elements if S are nonzero
S.TrueForAll( x => x != 0 );
I don’t see much of a difference in readability between the Python and the C#. They are merely different.
One of the very strange things about the .NET implementation is that Exists and TrueForAll aren’t members of the Array class, rather they are static members. So the invocation of the same thing on an Array is slightly different.
Array.Exists( S, x => x > 42 );
Array.TrueForAll( S, x => x != 0 );
C# 3.0 extension methods don’t help. Array inherits from IEnumerable. Nothing about its type is known for use by the compiler at compile time. You could try to implement Exists and TrueForAll, but you end up having to invoke like this:
S.Exists<int>( x => x > 42 );
S.TrueForAll<int>( x => x != 0 );
All this because I think about python. I should just write more boo, but alas, boo still doesn’t support Generics. Boo is great, but not for interoperating with Generic types from other .NET libraries.