Tuesday, October 26, 2010

More extension methods: To and In. Also, a static class to make Sequences.

It's been quite a while since I posted. I've been quite busy with work. But without further ado, here's a static class I've been working on (code after the break):

public static partial class Seq
{
    public static IEnumerable<T> Make<T>(params T[] list)
    {
        foreach (T item in list)
            yield return item;
    }

    public static IEnumerable<int> Make(int from, int to)
    {
        return Make(from, to, x => x + (from <= to ? 1 : -1));
    }

    public static IEnumerable<int> Make(int from, int by, int to)
    {
        return Make(from, to, x => x + by);
    }

    public static IEnumerable<int> Make(int from, int to, Func<int, int> by)
    {
        if (from <= to)
        {
            do
            {
                yield return from;
                from = by.Invoke(from);
            } while (from <= to);
        }
        else
        {
            do
            {
                yield return from;
                from = by.Invoke(from);
            } while (from >= to);
        }
    }

    public static IEnumerable<int> Make(int from, Func<int, int> by)
    {
        while (true)
        {
            yield return from;
            from = by.Invoke(from);
        }
    }

    public static IEnumerable<int> To(this int i, int to)
    {
        return Make(i, to);
    }

    public static IEnumerable<int> To(this int i, int to, Func<int, int> by)
    {
        return Make(i, to, by);
    }
}

This was somewhat inspired by F#'s Seq and Lists. It's not supposed to match, but I got the idea there. I also used this as an excuse to learn how to use yield return statements.

This allows you to quickly compose sequences. Right now, all but one are for Int32s. It's a known issue that there's no generic constraint for numeric types, so I can't make a generic out of these. I could theoretically make them Decimals, and just downcast using Cast(), but I don't care for that. So, I made ints, and if I ever need others, I'll copy/paste with doubles/bytes/whatever. The class is partial so it can easily be extended.

Here's a few more interesting extension methods, ones that I didn't feel that were attached to this class specifically but had external use.

public static partial class Extensions
{
    public static bool In<T>(this T i, IEnumerable<T> collection)
    {
        return collection.Contains(i);
    }

    public static bool In<T>(this T i, params T[] list)
    {
        return list.Contains(i);
    }

    public static void Print<T>(this IEnumerable<T> collection, TextWriter tw = null)
    {
        if(tw == null)
            tw = Console.Out;
        tw.Write("{ ");
        int max = collection.Count();
        for (int i = 0; i < max; i++)
        {
            tw.Write(collection.ElementAt(i).ToString() + " ");
            tw.Write(i == max - 1 ? "}" : ", ");
        }
        tw.WriteLine();
    }
}

In is a simple extension method. It basically wraps and reverses the LINQ extension method Contains.

So instead of doing collection.Contains(10), we can do 10.In(collection). Kinda Ruby-ish, in my opinion. I like the syntax. It's obvious, and in some contexts, it reads better. Actually, the forum thread on bytes that started the discussion that led to these methods has an example of where this scans better:

if(25.In(18, 25)) DoSomething();

Also, I've included a simple Print extension that prints any IEnumerable (though not always well), assuming that the type's ToString is defined properly.

Here's some example and test code:

class Program
{
    static void Main()
    {
        Console.WriteLine("1.To(10)");
        1.To(10).Print();
        Console.WriteLine("10.To(1)");
        10.To(1).Print();
        Console.WriteLine("10.To(10)");
        10.To(10).Print();
        Console.WriteLine("1.To(10, x => x + 2)");
        1.To(10, x => x + 2).Print();
        Console.WriteLine("Seq.Make(0, x => x + 5).Take(10)");
        Seq.Make(0, x => x + 5).Take(10).Print();
        Console.WriteLine("Seq.Make(0, 100, x => x + 3)");
        Seq.Make(0, 20, x => x + 3).Print();
        Console.WriteLine("Seq.Make(\"abc\", \"def\", \"ghi\")");
        Seq.Make("abc", "def", "ghi").Print();
        Console.WriteLine("10.In(1.To(10))");
        Console.WriteLine(10.In(1.To(10)));
        Console.WriteLine("\"test\".In(\"test\", \"ing\")");
        Console.WriteLine("test".In("test", "ing"));
        Console.ReadKey();
    }
}

No comments:

Post a Comment

Speak your mind.