iangilham.com

By on

software csharp

While reading Apprenticeship Patterns this morning, I started thinking about how functional programming concepts are often neater and more concise than purely imperative code patterns. In particular, when applied to large collections of objects. Having written several automation programs that perform a function on a large set of input files, I already had a perfect opportunity to play with a map function.

The basic premise is to apply a single operation to all the objects in a collection and return a new collection containing just the results, which are usually the same type of objects after mutation. In the purely functional sense, the operation should have no side effects for the source collection, though requirements will vary. Either way, a good implementation should be general enough to allow both cases by passing in the operation (a method) as one of the parameters.

I started with a search and turned up some interesting information put together for map functions using System.ThreadPool. Now that .NET version 4 is out, though, I felt that a more up to date implementation using the Task Parallel Library would be a fun little exercise.

Starting from Eric Sink’s code and working backwards, I ended up starting from scratch with a very simple Map function that takes an IEnumerable<T> and performs a function with a single parameter on each element in the enumerable. My main focus was on simplicity, since I had a bit of a fight with some of the more complex syntax of passing functions with generic types around. Here is what I ended up with:

/*
 * Copyright 2010 Ian Gilham
 *
 * Based on work by Eric Sink found here:
 * http://www.ericsink.com/entries/multicore_map.html
 *
 * You may use this code under the terms of any of the following
 * licenses, your choice:
 *
 * 1)  The GNU General Public License, Version 2
 *  	http://www.opensource.org/licenses/gpl-license.php
 * 2)  The Apache License, Version 2.0
 *  	http://www.opensource.org/licenses/apache2.0.php
 * 3)  The MIT License
 *  	http://www.opensource.org/licenses/mit-license.php
 *
 * This code is unsupported sample work and I have no interest in
 * maintaining, or hosting it.
 */
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
 
namespace IanGilham.ParallelUtils
{
    public static class Extensions
    {
        private static bool useThreads =
            (Environment.ProcessorCount > 1);
 
        public static IEnumerable Map(
            this IEnumerable source,
            Func function)
        {
            var result = new List();
            if (useThreads)
            {
                Parallel.ForEach(source, new Action((item) =>
                    {
                        result.Add(function(item));
                    }));
            }
            else
            {
                var resultList = new List();
                foreach (var item in source)
                {
                    resultList.Add(function(item));
                }
                result = resultList;
            }
            return result;
        }
    }
}