LINQ to Life

Queries Aren't Just for Databases Anymore

Believe it or not, LINQ is not just about data access. LINQ makes a profound impact on everyday code. Remember, the days trying to find an element in a list by looping through elements, setting some flag (e.g. found = true;), and calling break to get you out of the loop? Those days are over thanks to LINQ and the powerful features that come along with it including lambda expressions, type inference, and extension methods. This article will use a classic program, Conway’s Game of Life, as a chassis for a LINQ engine. The purpose is to show how writing query expressions adds flexibility, readability, and power to code that is typically full of loops and branches that add to the cyclomatic complexity.

The Rules of the Game

The “Game” itself, is a cellular automaton. The board is a grid, composed of cells. A cell can be on (alive) or off (dead). The state of a cell in any subsequent generation depends on its current state and the state of its neighbors according to a few simple rules:

A living cell will die of loneliness when it has 0 or 1 living neighbors.

A living cell will die of over crowding when it has 4 or more neighbors.

A living cell will survive when it has either 2 or 3 other living neighbors.

A dead cell, will be born when it has exactly 3 living neighbors.

I made a little screen cast that shows the Game in action. Please forgive the choppiness, I couldn't quite get my aging laptop to make a clean capture. However, if you download and run the files, you'll see a smooth display of some of the intricate patterns that can be made simply by following those basic rules. Warning: it’s slightly mesmerizing.

The Facts of Life

The Cell is Ruled by the Dish

The cells themselves are represented by the class below:

The Age field keeps track of the number of generations the cell has been “alive”. If the cell has an Age of zero, it’s dead. Normally, you would just keep track of a Boolean flag (e.g. IsAlive). In this particular implementation, we'll keep track of the Age for a rule modification described below.

The Neighbors property, is a little implementation detail that helps cache the list of neighboring cells. It helps calculate things like the NumberOfLivingNeighbors quickly, that fits nicely with the rules.

The Position serves as a unique identifier for the cell itself. Every cell gets a unique integer value for Position that corresponds to the order in which it was added to the list. Essentially, this is the index. An interesting note is that concept of rows and columns in a grid isn't controlled by the cell itself. That honor belongs to the grid itself. In fact, based on the cells position, the grid manages the row/column relationship and tells the cells who its neighbors are.

That grid is represented by a class named PetriDish:

A client (i.e. the GUI) simply news up a dish, giving it a height and width and calls NextGeneration for each frame that it draws. The NextGeneration method runs the actual rules and alters the state of the cells.

For readability, and to match the rules above more closely, the NextGeneration method breaks up the processing into two sections: rules for living cells and rules for dead cells.

Rules for Living Cells

The PetriDish holds on to cells it creates using a simple List<Cell>. Primarily because LINQ loves sequences, specifically anything IEnumerable<T>. On that list, we run LINQ queries to determine from the living cells, who dies and who survives.

The first query expression finds all the cells in the list that are alive by filtering on Age > 0. The next two query expressions actually use the livingCells expression to apply Conway’s logic. This is called “query chaining” and makes writing some really complicated queries very readable as it minimizes the needs for joins.

The good news is that the actual source, the list in this case, is only traversed when you start to pull information out of it. That’s big! There are no in-memory traversals, no temporary tables, nothing that consumes anything until you absolutely need it because query expressions work by building expression trees that are only executed against the source when used. This magic is known as deferred execution.

After the rules are defined in cellsToKeepAlive and cellsToKill you can use standard iteration techniques such as foreach, but a more LINQ-y/3.5-ish way to do this is with an extension method and a lambda function like the Each method.

The Each method is something I wrote. Extension methods in general let you “spot weld” methods on classes without using inheritance.

This particular extension method spot welds an Each method on anything that implements IEnumerable<T>. You know this because the first parameter to this method defines what this will be inside the method body. Action<T> is a pre-defined class that basically stands in for a function (delegate) returning no value. Inside the method, is where the elements are extracted from the list. What this method enables is for me to cleanly apply a function in one line of code.

... simply iterate through the cells that match and set the flag LivesInNextGeneration appropriately. The code c => c.LivesInNextGeneration = false; is a lambda function. Lambda functions let you write very concise anonymous methods. Type inference comes into play here allowing me to avoid typing Cell c => .... The compiler determines that c is of type Cell because the Each extension is returning an IEnumerable<Cell>.

Marking the cell this way is a design decision that allows the code to persist the decision without modifying the collection by modifying the Age of the cell. That update, or change in the cells Age, happens separately. Another way to accomplish this would have been to build separate lists for cellsToKill and cellsToKeepAlive.

Rules for Dead Cells

The NextGeneration method then applies the rules that apply to dead cells.

///<spanclass="code-SummaryComment"><summary></span>/// Applies the rules to non living cells.
///<spanclass="code-SummaryComment"></summary></span>privatevoid ApplyRulesToNonLivingCells()
{
// a non-living cell can only be born
// a living cell can either die or survive
var notLivingCells =
from c in _cells
where c.Age 0select c;
var pregnantCells =
from c in notLivingCells
where c.NumberOfLivingNeighbors 3select c;
pregnantCells.Each(c => c.LivesInNextGeneration = true);
}

Query chaining is used again to select cells from the dead cells in the current generation that will be born in the next generation. Those cells are marked so that they will be alive in the next generation.

Updating the State

The age is effected after the rules have been applied. The cell list is queried based on the LivesInNextGeneration marker, and the appropriate method is called. The GiveLife method simply adds one to the Age. The TakeLife method simply sets the Age to zero.

Notice the Except method excluding the cells that are also on the dyingOfOldAge list. It allows me to perform what essentially amounts to a SQL NOT IN filter inside a LINQ query.

Giving life to the non-living neighbors is a little more tricky.

// old age cells spawn by making
// any non-living neighbors alive
var spawnedCells =
from old in dyingOfOldAge
from n in old.Neighbors
where
n.Age == 0select n;

To get the “non-living neighbors”, we first start from the cells that are dyingOfOldAge. Without a join, you can use another sequence that is attached to the item in the first sequence (e.g. Neighbors), and select from that new sequence.

The code at the end of that function that marks the cells as LivesInNextGeneration or not then becomes:

Conclusion

I may never write a for loop again. At least not while trying to find a particular element within a collection. LINQ expressions, extension methods, lambda expressions, and type inference are just a few of the powerful new features of 3.5 that make C#/VB.NET so much more expressive: not just for your typical database backed LOB application, but for everyday coding.

In this article, I only touched on some of the critical places where I used LINQ. Please download the source for this project and look at even more LINQ-y goodness. There is even an example of using the Enumerable.Range method that provides Ruby style sequence generation. Warning: I didn't spend much time on the GUI itself. The code would also make a good basis for a round or two of code golf, as the point of this article is to show how readable and clear LINQ can make your code.

Comments and Discussions

The cells and Grid diagram for description doesnt load, when tried to open the image url, it said the page doesnt exist anymore, since am going through this for the first time, would like to know the picturization of the class diagram as well.Thanks.

Just to let you know this article has helped me immeasurably in understanding just what exactly LINQ is. Thankyou for such a simple illustration and bringing back to life life (sorry for the pun lol) been a long time since i tried to write a game of life

I finally got a chance to get back to this. I remember now why I wrote the code like I did. I explicitly wanted to state the rules of life in the LINQ queries (i.e., a living cell stays alive if it has 2 or 3 neighbors). Your suggestion is a good one though. There are a lot of places where this code could be trimmed by quite a bit. Like I said, it's ripe for a round or two of code golf -- hmmmm....perhaps another article, or a blog post.

Thank you for the article! As a newbie in both C# and LINQ but with strong C++ experience I was looking for a code that shows "how do they do it .NET world" And I must say, the quality of your code is a big rarity here at codeproject.

This is probably the smallest and cleanest implementation of the game of life I've seen.

As for LINQ, it has saved my ass on several occasions when I have had tight deadlines to meet. My one complaint about LINQ relates to LINQ-to-SQL... specifically the INSERT and UPDATE functionality... I have been continuously irritated by the fact that once you retrieve data, you have to use the same datacontext object to modify it and commit the changes. This has led to some weak coding on my part - I would love to be able to retrieve the objects representing the data in the far back-end, pass them up to the code-behind, manipulate them, and then commit the changes.

What sucks is that you need to use the original datacontext object to do so... so even if you create another datacontext object that is identical, it won't accept a commit if the representative object is from another instance of the datacontext.

What this leads to is bad code, where the datacontext is retrieved, the query performed, and the updates committed, all in the same layer, which is usually the code-behind in an ASP.NET app.

This problem probably doesn't matter so much in desktop apps where you don't have issues with maintaining state. I guess in an ASP.NET app you could serialize your datacontext and store them in a session, but I just have a feeling this would be slow...

Cheers,

Sam

p.s. i gave you a 5/5 rating. My criticisms are only of LINQ, and not of your app.

Thank you so much for your comments and rating. They are highly appreciated.

Regarding LINQ to SQL, you're right there is a definite danger that people will write LINQ queries against the database directly in their code behind. That brings the usual gammit of "Duplicate Code" and other fragrant styles.

Reminds me of a joke (fair warning):

Patient: Doctor, doctor, I broke my arm in three places.Doctor: Stay out of those places.

My viewpoint, and what I've been doing, is to take the DAL code (LINQ to SQL) and put it safely away in it's own class or tier. Follow your favorite data access pattern: Active Record, Data Mapper, Repository, et. al. That should mop up some of that messy code.

Having a stateless app does pose an extra bit of complexity, as you noted. You will have to keep the object's "key" around, so that you can do a retrieval first, make the updates based on the pages fields, and then call update on the data context -- sucks being stateless.

I have been using Linq for some time so I am not a beginner, however your code was elegant enough to be worthy of printing out and giving careful study to learn new concepts. The "Each" extension method was especially elegant. I mean you could call a method but look how clean the code looks. And simply calling "Enumerable.Range..." that seems so obvious... after someone shows you of course

I wish I could take full credit for as the Each method. Although, I did in fact, write the particular implementation you see, I remember someone, trying to do the same thing. I forget how they did it, it was slightly different. We were both inspired by Ruby syntax in this regard. Essentially, it's a missing method that should be part of the IEnumerable<T> extensions (and probably will in the next ver.)

I'm thinking about starting a simple library on CodePlex or something that has these little common helpers. Another common one that I can think of would be an extension on int so that you could essentially pull of the the Ruby trick and say something like 4.Times( delegate { Console.WriteLine("Thank You!"); }); and the famous 20.Minutes().Ago()

Actually, a method similar to "Each" does exist on the List<t>, and it's called "ForEach".

I think the ForEach() was not included in IEnumerable<t> on purpose because it's behavior is different from the other LINQ methods, because it actually enumerates (executes) an IEnumerable<t>, whereas the other LINQ methods are aimed at querying a datasource (hence the Q in LINQ) and can be compiled into an expression tree.

ForEach() can't be compiled to an expression because it is executed immediately, so probably left out to avoid confusion, and (hopefully) will not be there in newer versions either, at least not in the LINQ namespace

This was a good demo of ways to use LINQ, I personally think that collection manipulation will be the place where most of my LINQ resides.

On thing though, the code sample shown for your 'Each' extension method is missing <T> for the Each, IEnumerable and Action - this may be confusing as the explanitory text below refers to the generic versions.

Other than that, some great ideas.

C

Edit: The reason for this is probably the same as why when I first posted this message the <T> wasn't displayed! It gets swallowed up as a tag I guess - I got it to display by entitizing the < and > by using '& l t ;' and '& g t ;', but without the spaces.

Great Code! You did not only show the topic but also demonstrated a perfect coding style! Every .NET Code should look like yours. Comments, unit tests, regions, perfect project structure...And by the way, you showed what you wanted to show, it was very easy to follow your idea!Man, you get my 5!!!