A List Comprehension is a type of set notation in which the programmer can describe the properties that the members of a set must meet. It is usually used to create a set based on other, already existing, set or sets by applying some type of combination, transform or reduction function to the existing set(s).
Consider the following problem: You have a sequence of 10 numbers from 0 to 9 and you need to extract all the even numbers from that sequence. In a language such a C# version 1.1, you were pretty much confined to the following code to solve this problem:
1 2 3 4 5 6 7 8 9 10 11 | ArrayList evens = new ArrayList(); ArrayList numbers = Range(10); int size = numbers.Count; int i = 0; while (i < size) { if (i % 2 == 0) { evens.Add(i); } i++; } |
The code above does not show the implementation of the Range
function, which
is available in the full code listing below. With the advent of C# 3.0 and the
.NET Framework 3.5, a List Comprehension notation based on Linq is now available
to C# programmers. The above C# 1.1 code can be ported to C# 3.0 like so:
1 2 | IEnumerable<int> numbers = Enumerable.Range(0, 10); var evens = from num in numbers where num % 2 == 0 select num; |
And technically speaking, the C# 3.0 code above could be written as a one-liner
by moving the call to Enumarable.Range
into the Linq expression that generates
the evens
sequence. In the C# List Comprehension I am reducing the set
numbers
by applying a function (the modulo 2) to that sequence. This produces
the evens
sequences in a much more concise manner and avoid the use of loop
syntax. Now, you may ask yourself: Is this purely syntax sugar? I don't know,
but I will definitelly investigate, and maybe even ask the question myself at
StackOverflow. I suspect that this is not just syntax sugar and that
there are some true optimizations that can be done by utilizing the underlying
Monads.
I also posted this as an answer to a question on StackOverflow.com. The full code listing is available below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace CSharpListComprehensions { public class Program { public static void RunSnippet() { Run11Version(); Run35Version(); } public static void Run11Version() { ArrayList evens = new ArrayList(); ArrayList numbers = Range(10); int size = numbers.Count; int i = 0; WL(""); WL("Numbers:"); foreach (int x in numbers) { WL(x.ToString()); } while (i < size) { if (i % 2 == 0) { evens.Add(i); } i++; } WL(""); WL("Evens:"); foreach (int y in evens) { WL(y.ToString()); } } public static void Run35Version() { IEnumerable<int> numbers = Enumerable.Range(0, 10); WL(""); WL("Numbers:"); foreach (int x in numbers) { WL(x.ToString()); } var evens = from num in numbers where num % 2 == 0 select num; WL(""); WL("Evens:"); foreach (int y in evens) { WL(y.ToString()); } } #region Range Function public static ArrayList Range(int end) { return Range(0, end); } public static ArrayList Range(int start, int end) { ArrayList al = new ArrayList(); for (int i = start; i < end; i++) { al.Add(i); } return al; } #endregion #region Helper methods public static void Main() { try { RunSnippet(); } catch (Exception e) { string error = string.Format("---\nThe following error occurred while executing the snippet:\n{0}\n---", e.ToString()); Console.WriteLine(error); } finally { Console.Write("Press any key to continue..."); Console.ReadKey(); } } private static void WL(object text, params object[] args) { Console.WriteLine(text.ToString(), args); } private static void RL() { Console.ReadLine(); } private static void Break() { System.Diagnostics.Debugger.Break(); } #endregion } } |