Posted on 6/17/2008 1:08:27 AM by Justin Etheredge
I have added a few more methods to Dizzy recently, namely Concatenate (which I am considering renaming to Chain), Reverse, and Partition. None of these methods were particularly difficult to implement, but they are interesting nonetheless. The most interesting of them is Partition, which simply splits up a list using a Predicate delegate. In case you are not in the know, a Predicate delegate is simply a delegate that takes some value and returns a boolean. Its signature looks like this:
public static Pair<IEnumerable<T>>
Partition<T>(this IEnumerable<T> list, Predicate<T> predicate)
So, you take a list and then call something like:
var nums = new List<int> {2, 5, 6, 9, 4, 10, 1};Pair<IEnumerable<int>> splitNums = nums.Partition(n => n > 5);
If you had a list of integers, this would take your list and split it into two lists. One that has all numbers greater than 5 and the other that has numbers less than 5. Pretty easy stuff, nothing ground breaking.
The slightly more interesting part is in how we are returning two lists. We have declared a new class called Pair, which is named the same as the Pair class already in .net, only it is generic. For my purposes I only used a single generic argument, and this was so that I could implement IEnumerable<T> on the Pair class. The reason why this is interesting is that if I wanted to split a list into two, and I wanted to do further processing on it without IEnumerable then I would have to assign the result to an intermediate variable. I wouldn't be able to iterate over the result of the list. The Pair class looks like this:
public class Pair<T> : IEnumerable<T>
{ public T First { get; set; } public T Second { get; set; }
public Pair()
{
}
public Pair(T first, T second)
{ this.First = first;
this.Second = second;
}
IEnumerator IEnumerable.GetEnumerator()
{ return ((IEnumerable<T>)this).GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{ yield return First;
yield return Second;
}
}
What this allows me to do is this:
nums.Partition(n => n > 5).ForEach(l => l.ForEach(Console.WriteLine));
Here, I am taking my list of numbers and I am splitting it into numbers below and above 5. Then I am feeding the result into ForEach which will loop through both lists, and then pass those lists into ForEach to loop through each integer. This will output this (notice all of the integers that match the predicate get pushed to the front):
In the future I plan on implementing a method in Dizzy that will make doing these sub-iterations a bit easier.
You may be looking at this and saying "What good does it do to split a list into two only to put it back together?" Well, first of all it could be used to reorder things to the front of a list, secondly, I have a few ideas for other methods that I can implement to operate on the Pair<T>. One idea is for a "Fork" method that would take both lists and allow you to do separate processing on each list.
Hopefully this method will help you out, or at least give you a few ideas! If you haven't already, check out Dizzy!
Posted on 6/15/2008 3:28:25 AM by Justin Etheredge
I have added an "AsParallel()" method to Dizzy which mimics the way that the Parallel Extensions to the .net framework works. So, if you want to call a parallel map function you simply need to do this:
list.AsParallel().Map(n => n.ToUpper());
The AsParallel() method just looks like this:
public static IParallelEnumerable<T> AsParallel<T>(this IEnumerable<T> list)
{ return new ParallelEnumerable<T>(list);
}
As you can see, it just takes an IEnumerable<T> and replaces it with a IParallelEnumerable<T>. This Interface/Class combo doesn't provide any sort of work, it is just a flag to use the Parallel version of methods. Currently "Map" is the only method with a parallel version. The ParallelEnumerable class just looks like this:
public class ParallelEnumerable<T> : IParallelEnumerable<T>
{ private IEnumerable<T> enumerable;
public ParallelEnumerable(IEnumerable<T> list)
{ if (list == null) throw new ArgumentNullException("list"); this.enumerable = list;
}
IEnumerator IEnumerable.GetEnumerator()
{ return this.enumerable.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{ return this.enumerable.GetEnumerator();
}
}
So, you can see that we are just wrapping an IEnumerable and returning the items we need to support the IEnumerable interface. Then we just define a "Map" method that takes an IParallelEnumerable instead of an IEnumerable and voila! Instant parallelism without the need for different method names. The map method is just the same as it was in this post, only the method signature is now change to look like this:
public static IEnumerable<TResult> Map<TArg, TResult>(
this IParallelEnumerable<TArg> list,
Func<TArg, TResult> func)
So, whenever we have an IParallelEnumerable we will call this method instead of our other Map method. We still return an IEnumerable because we don't want additional methods chained to this call to automatically try to use parallel versions.
So, I hope you found this useful, and I hope you also check out Dizzy. I've added a few other methods, and plan to keep adding methods regularly. If you have any ideas for new methods, please let me know.
Posted on 6/11/2008 3:13:58 AM by Justin Etheredge
I just checked in a Zip method to Dizzy. This method takes n number of lists and returns a list of lists containing the nth item in each list. Go that? Ha. Let me show you:
Did that clear it up? Good. Lets take a quick look at the code (before you freak out, scroll down below the code):
public static IEnumerable<T[]> Zip<T>(this IEnumerable<T> list, params IEnumerable<T>[] lists)
{ if (list == null) throw new ArgumentNullException("list");
int enumeratorsCount = lists == null ? 1 : lists.Length + 1;
var enumerators = new IEnumerator<T>[enumeratorsCount];
enumerators[0] = list.GetEnumerator();
if (lists != null)
{ for (int i = 0; i < lists.Length; i++)
{ if (lists[i] == null)
throw new ArgumentNullException("lists", String.Format("Item at index {0} is null", i)); enumerators[i + 1] = lists[i].GetEnumerator();
}
}
return ZipImplementation(enumeratorsCount, enumerators);
}
private static IEnumerable<T[]> ZipImplementation<T>(int enumeratorsCount, IEnumerable<IEnumerator<T>> enumerators)
{ for (;;)
{ int current = 0;
var result = new T[enumeratorsCount];
foreach (var enumerator in enumerators)
{ if (!enumerator.MoveNext())
yield break;
result[current++] = enumerator.Current;
}
yield return result;
}
}
Look a little crazy? Why is it two methods? Well, the first method basically just gets a list of all enumerators that we are going to loop through. It also checks the list to see if it is null. This method exists so that all of these checks will be done when the method is first called. Then we call ZipImplementation which uses "yield return" and so it is not executed right away. ZipImplementation is what does all of the heavy lifting for the function. It loops through the array of enumerators adding each item to the current sub list in our result. If we run out of items in any iterator then we just call "yield break" and quit.
If we successfully fill the current array then we "yield return" it. And that is pretty much it, I hope you find the method useful and go check out Dizzy if you feel the urge!
Posted on 6/10/2008 1:04:32 AM by Justin Etheredge
I just published the first source for "Dizzy" my new library of higher order methods. Its focus right now is on lists, which is the reason I called it dizzy. Looping through lists makes me think of spinning, which in turns makes me think dizzy. I thought it was clever! Anyways, I have pushed the project with just a very few methods. I am using NUnit and right now I have 100% unit test coverage which is easy since they are just library method calls.
Right now I have a total of 5 methods. They are Map, ForEach, Filter, Repeat, and Cycle. More will be added in the next few days and I will blog about methods as I add them. Right now I am looking at adding the usual list processing methods from other languages, but if you are interested in seeing any particular methods implemented, please let me know. Or implement them yourself and submit them to me!
If you want to check out Dizzy, just click the link!