|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Queries Aren't Just for Databases AnymoreBelieve 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. The Rules of the GameThe “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:
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. Click here to watch the screen cast.The Facts of LifeThe Cell is Ruled by the DishThe cells themselves are represented by the class below:
The The The That grid is represented by a class named
A client (i.e. the GUI) simply news up a dish, giving it a For readability, and to match the rules above more closely, the Rules for Living CellsThe /// <summary>
/// Applies the rules to living cells.
/// </summary>
private void ApplyRulesToLivingCells()
{
// a living cell can either die or survive
// a non-living cell can only be born
var livingCells =
from c in _cells
where c.Age > 0
select c;
var cellsToKill =
from c in livingCells
where
// loneliness
c.NumberOfLivingNeighbors <= 1 ||
// over population
c.NumberOfLivingNeighbors >= 4
select c;
var cellsToKeepAlive =
from c in livingCells
where
c.NumberOfLivingNeighbors 2 ||
c.NumberOfLivingNeighbors 3
select c;
cellsToKill.Each(c => c.LivesInNextGeneration = false);
cellsToKeepAlive.Each(c => c.LivesInNextGeneration = true);
}
The first query expression finds all the cells in the list that are alive by filtering on 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 The public static class IEnumerableExtensions
{
public static void Each<T>(
this IEnumerable<T> source,
Action<T> action)
{
foreach(T item in source)
{
action(item);
}
}
}
This particular extension method spot welds an The lines of code… cellsToKill.Each(c => c.LivesInNextGeneration = false);
cellsToKeepAlive.Each(c => c.LivesInNextGeneration = true);
... simply iterate through the cells that match and set the flag Marking the cell this way is a design decision that allows the code to persist the decision without modifying the collection by modifying the Rules for Dead CellsThe /// <summary>
/// Applies the rules to non living cells.
/// </summary>
private void 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 0
select c;
var pregnantCells =
from c in notLivingCells
where c.NumberOfLivingNeighbors 3
select 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 StateThe age is effected after the rules have been applied. The cell list is queried based on the /// <summary>
/// Update cells effects the age of the cells in the dish
/// </summary>
private void UpdateCells()
{
// giveth life
var livingCells =
from c in _cells
where c.LivesInNextGeneration
select c;
livingCells.Each(c => c.GiveLife());
// taketh it away
var dyingCells =
from c in _cells
where !c.LivesInNextGeneration
select c;
dyingCells.Each(c => c.TakeLife());
}
Playing God: New Rules for LifeIn the tradition of the “Game of Life” (everyone comes up with his/her own rules), and to see how easy it is to change the code; I made up “The Spawning Rule”. This rule states:
In the var dyingOfOldAge =
from c in cellsToKeepAlive
where
c.Age >= AgeLimit
select c;
These are cells that would have aged another generation in the next update, but have already reached the spawn rule’s age limit. Therefore, I have to remove them from the list of cells to keep alive. // remove the old-age cells
cellsToKeepAlive = cellsToKeepAlive.Except(dyingOfOldAge);
Notice the 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 == 0
select n;
To get the “non-living neighbors”, we first start from the cells that are The code at the end of that function that marks the cells as cellsToKill.Each(c => c.LivesInNextGeneration = false);
dyingOfOldAge.Each(c => c.LivesInNextGeneration = false);
cellsToKeepAlive.Each(c => c.LivesInNextGeneration = true);
spawnedCells.Each(c => c.LivesInNextGeneration = true);
ConclusionI may never write a 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
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||