0

I've been teaching myself a little LINQ and an exercise I thought would be useful was to take my existing Project Euler C# code, which I built using Test Driven Development and gradually convert it to LINQ. I realise that LINQ is not always the best solution for all of the Project Euler problems, but I don't want to get into that here.

I'm wondering whether or not it's feasible to refactor "traditional" OO C# code to use LINQ and functional programming syntax whilst keeping all of your tests passing. I can't find a way to make the tiny steps I'm used to making using TDD when converting to LINQ and this is a roadblock for me. I seem to have to make large changes to come up with a single function that I then replace whole chunks of my code with.

I realise I could write this from scratch in LINQ, but in the real world, I'd like to be able to replace parts of my existing C# code to take advantage of LINQ where appropriate.

Has anyone been successful with this approach? What resources did you find useful for refactoring existing C# code to use LINQ whilst taking a Test Driven Development approach?

3 Answers 3

4

If your implementation can be expressed as a series of transforms on lists of objects then this should be fairly easy to do piecemeal, if it is wholly dependent on maintaining state then it will not be easy.

That's an obtuse statement. Let me explain. If you have code that looks something like this:

var multiples = new List<int>()
for(var i=0;i<1000;i++)
  if(i%3==0 || i%5==0)
    multiples.Add(i);

var sum = 0;
foreach(var m in multiples)
  sum += m;

This is expressible as three distinct set-based operations

  1. Generate all numbers between 0 and 1000 (this is done implicitly in this case by the for loop, but you can imagine having a for loop fill up an array that you then iterate through to get the multiples)
  2. Filter out only the ones that are multiples of 3 or 5
  3. Aggregate those by adding

You could actually refactor this into 3 methods that roughly looked like that and they would correspond roughly to the Linq methods Enumerable.Range(), enumerableInstance.Where(), and enumerableInstance.Aggregate(). So yes, you can do it there.

If however your implementation looks like this:

var sum = 0;
for(var i=0;i<1000;i++)
  if(i%3==0 || i%5==0)
    sum += i;

You would be harder pressed to see the set-operations and therefore be able to break this up into them.

I'm going to go ahead and say, if you can refactor your implementation to a bunch of static methods that do not depend on any static variables, and take as inputs only immutable enumerations (IEnumerable<T> or arrays) or lambdas/delegates, and return only enumerations then converting each piece individually to LINQ should be easy as pie. Otherwise, it's not going to be because it's a different paradigm, one of filtering collections as opposed to managing state explicitly.

Also, as you say, in a lot of cases the Project Euler stuff will not run efficiently with LINQ-to-objects, to help this somewhat you should look into using the PLINQ extensions which can parallelize your query. This is still not quite the same as a true functional language which would also analyze the expression tree and basically compile your statements down to efficient procedural code, but it's a lot closer.

1
  • Agreed, getting things into a composable form is definitely the first, and most important, step.
    – FinnNk
    Jun 29, 2011 at 23:18
3

I've been using ReSharper recently, and it has auto-refactoring of foreach style loops into linq statements. You can try it out for free and see if it works for you.

1
  • That's not really the essense of LINQ, that's just moving a keyword into an extension method. LINQ is about set operations, not replacing everything with extension methods - matter of fact, doing so indiscriminately is probably the #1 mistake people make while learning it. Jul 1, 2011 at 3:54
2

Typically a Project Euler problem revolves around a single algorithm or chain of algorithms (at least the ones I've done have been). If your unit tests are testing the observable behaviour rather than the implementation then you shouldn't need to (and in fact you don't want to) touch the tests at all. For example, if you've implemented a prime sieve your tests cases shouldn't care whether your implementation is OO or functional - the output will be the same.

If you're really worried about having missed edge cases when writing the original tests you could look at something like Pex which can analyse code and produce cases that cover all possible code paths.

2
  • Thanks for the suggestion, I don't necessarily want to change my tests, but how do I change my code and keep all the tests passing without the change to the code being a wholesale change? In other words, what, if any, are the intermediate steps between the OO C# and the LINQ C#? Jun 29, 2011 at 22:32
  • It's difficult to answer without either a specific scenario or code, but as an example quite a few of the problems will involve finding factors or sieving for primes or other common tasks. Redo these first and then compose into the building blocks of the bigger solutions. Bear in mind that if you're changing the implementation of a single algorithm into a different paradigm then it's likely to be a "wholesale" change anyway, so don't worry about it if you go from 5 passing tests to 1 to 5 again as you refactor. In a real system you'd be changing the equivalent of these things as units anyhow.
    – FinnNk
    Jun 29, 2011 at 22:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.