Collection comprehension

F# (and Ruby, I am told) has a nice little syntactic feature called “comprehension” which allows developers to create lists of numbers or characters by specifying a range to be “expanded”, like this:

let numbers = [0 .. 9]
let alpha = ['A' .. 'Z']
let everyOtherNumber = [0 .. 2 .. 16]

printfn "%A" numbers // [0; 1; 2; 3; 4; 5; 6; 7; 8; 9]
printfn "%A" alpha // ['A'; 'B'; 'C'; ... 'X'; 'Y'; 'Z']
printfn "%A" everyOtherNumber // [0; 2; 4; 6; 8; 10; 12; 14; 16]

Today I’ve been working on an e-commerce project where I need to build a list of months, so I started looking for a comprehension equivalent in C# and found this less elegant but adequate (for my purposes) method on Enumerable that does something similar:

IEnumerable<Int32> months = Enumerable.Range(1, 12);

There are a few problems with Range, however:

  1. the signature is (int start, int count) instead of (int start, int end)
  2. Range only accepts and returns integers
  3. Range does not provide a means to “step” values

If anyone knows a better comprehension alternative in C#, please let me know!

3 thoughts on “Collection comprehension

  1. So good to see you enjoying the nice techniques from functional programming. I cannot speak to C#, but in Java I usually imitate what I normally get in things like Haskell and Scheme by creating my own enumeration objects.

    One way to look at comprehensions is a syntactic layer on top of loops. Since enumerable objects are the looping equivalent in OOP languages, I generally give up on the syntactic convenience and focus on getting the same concept. It is not the best, but without Syntactic Abstraction you usually cannot get much better.

    However, if you want to see the theory and practical variations of how things like this work, I recommend the following in order:

  2. Awesome, thanks for the links Aaron, I will have to check those out. What I probably should do is crack open Reflector and take a look at the C# equivalent of F# comprehension… and maybe do an IL comparison as well, just for kicks.

  3. lmf232s says:

    Im not saying its clean but you do get the same results.

    var months = Enumerable.Range(1, 12);
    char[] alpha1 = Enumerable.Range(‘a’, ‘z’ – ‘a’ + 1).Select(i => (Char)i).ToArray();
    char[] alpha1 = Enumerable.Range(‘k’, ‘z’ – ‘a’ + 1).Select(i => (Char)i).ToArray();
    var everyOtherNumber = Enumerable.Range(0, 16 + 2).Where(f => f % 2 == 0);

    months // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    alpha1 // [a, b, c, ………… x, y, z]
    alpha2 // [k, l, m, ………… x, y, z]
    everyOtherNumber // [0, 2, 4, 6, 8, 10, 12, 14, 16]

Leave a Reply

Your email address will not be published. Required fields are marked *