SgDotNet
Singapore Professional .NET User Group -For Cool Developers

Disrupting long complicated loops that are refactored into methods

rated by 0 users
This post has 11 Replies | 2 Followers

Top 10 Contributor
Posts 2,284
icelava Posted: 09-25-2006 6:13 PM

When writing a long routine, I prefer to refactor logical blocks out into their own methods with descriptive names. I also do this as much as possible with loop constructs, but there is one (type of) issue that complicates the matter:

Loops, in their natural real-world flow of logic, tend to have conditions that call for a breakage mid-stream to prevent further processing.

loop (whatever condition)
{

   // 1st block of work
    if (1st condition)
       break;
   // 1st block of work

   // 2nd block of work
    if (2nd condition)
       continue;
   // 2nd block of work 

}

So refactoring can make it turn out like

loop (whatever condition)
{
    this.Do1stWork();
    this.Do2ndWork();
}

By repositioning code into isolated methods, they are more readable in the overview of the loop, but they also lose knowledge of the loop they exist in, and thus lose control. What I have to do is then get the methods to return some form of enum or bool to communicate back to the loop what has happened inside the method.

Is there some other form of loop control others have implemented elegantly? 

The melody of logic will always play out the truth. ~ Narumi Ayumu, Spiral

Top 10 Contributor
Posts 1,221
In C++, we can use function pointers (functors) to do what you might want to do, but this is for certain cases only, for example the condition is based on some enumeration. We can actually extend the idea of using functors for the condition, but I'm not too sure about this. :) Will have to experiment it a bit.
Regards, triplez ------------------------------ http://triplez.mine.nu/blogs
Top 10 Contributor
Posts 2,284
triplez:
In C++, we can use function pointers (functors)
Not something applicable to C# and .NET Framework. I am interested to know how others try to address this issue. Those who are interested in writing readable code, of course.

The melody of logic will always play out the truth. ~ Narumi Ayumu, Spiral

Top 500 Contributor
Posts 4

icelava:
triplez:
In C++, we can use function pointers (functors)
Not something applicable to C# and .NET Framework. I am interested to know how others try to address this issue. Those who are interested in writing readable code, of course.

Delegates in C# have the functionality of function pointers in C++.

If the condition is an integer, you can try storing the delegates in an array table and execute them.

Top 10 Contributor
Posts 2,284

If it is similar to Delegates then I understand; thought it is something more impressive.

Even then with the use of delegate functions, how is one going to insert the necessary control logic? The breaks and continues. Calling an "unknown" method versus an internal method does not bring out a great deal of difference to solve the problem at hand.

The melody of logic will always play out the truth. ~ Narumi Ayumu, Spiral

Top 500 Contributor
Posts 4
icelava:

If it is similar to Delegates then I understand; thought it is something more impressive.

Even then with the use of delegate functions, how is one going to insert the necessary control logic? The breaks and continues. Calling an "unknown" method versus an internal method does not bring out a great deal of difference to solve the problem at hand.

I am not very sure about what control logic you are inserting but you can try the following?

throw an exception when you do not want the loop to continue OR pass a reference to the condition into the delegate function OR return some condition?

 What I was referring is to remove the { if (cond) then execute() } in the logic flow by throwing something like { table[cond].Invoke() }

Top 10 Contributor
Posts 2,284

You need to fall back and understand the context of the original situation: a logical block that cannot be easily split up but has an intermediate condition that demands for the breakage of the loop. If that block is refactored out into a readable method of its own, the method will lose knowledge of the loop it resides in. The "most elegant" way I have done is to return status Enums to continue controlling the loop.

As much as I try to structure code not to fall into this programming trap, I unfortunately still see it happening quite often, especially with existing code base that i must modify without a design overhaul. So whether it is a regular method or a table-driven delegate, that still calls for splintering of code blocks which is not always possible.

An example of this would be a series of objects used to do the work and test the condition; if the condition passes, continue to perform rest of work; if it fails no need to continue loop. Breaking this into two method calls in the main loop block would just result in

  1. need to have those series of objects instantiated in the main loop block
  2. passing all the references to both methods
  3. doing the check in the main loop block
  4. in the end, defeats the purpose of refactoring into a single method to make it more logical and readable

I do not favour the use of exceptions as part of regular control flow. That is another topic altogether, so i do not wish to digress further other than to state that these condition checks do not necessarily constitute as errors or problems in the system.

The melody of logic will always play out the truth. ~ Narumi Ayumu, Spiral

Top 10 Contributor
Posts 1,221

That's something to think about. Delegates will minimize the amount of code shown in the control loop, but I'm not too sure whether you can get out of the "break" and "continue" controls, unless you create something else. It is certainly an interesting problem to solve for code elegance.

I can try mocking up some code tomorrow to try out the possible ways to do things, but I forsee delegates and generics in play here, and a few classes required to provide this functionality.

Furthermore, I do not favour exceptions, and technically speaking, there are a whole load of other issues with regards to exceptions. It is shocking to even suggest to use exceptions. Just a point to note that exceptions are not meant to be thrown regularly and should be reduced to a minimum (in terms of probability of being thrown) in your code.

Regards, triplez ------------------------------ http://triplez.mine.nu/blogs
Top 500 Contributor
Posts 4
triplez:

That's something to think about. Delegates will minimize the amount of code shown in the control loop, but I'm not too sure whether you can get out of the "break" and "continue" controls, unless you create something else. It is certainly an interesting problem to solve for code elegance.

I can try mocking up some code tomorrow to try out the possible ways to do things, but I forsee delegates and generics in play here, and a few classes required to provide this functionality.

Furthermore, I do not favour exceptions, and technically speaking, there are a whole load of other issues with regards to exceptions. It is shocking to even suggest to use exceptions. Just a point to note that exceptions are not meant to be thrown regularly and should be reduced to a minimum (in terms of probability of being thrown) in your code.

Agreed that exceptions are not meant to be thrown regularly, I am trying to suggest possibilities. Take for example, if you can guess the probability of the break vs the continue let say 99% to 1%, the exceptions raised is still minimum if you set it to raise at that 1% level. Definitely this is not good if break and continue is something like 50/50 then you have 50% exceptions all the time.

Top 500 Contributor
Posts 4
icelava:

You need to fall back and understand the context of the original situation: a logical block that cannot be easily split up but has an intermediate condition that demands for the breakage of the loop. If that block is refactored out into a readable method of its own, the method will lose knowledge of the loop it resides in. The "most elegant" way I have done is to return status Enums to continue controlling the loop.

As much as I try to structure code not to fall into this programming trap, I unfortunately still see it happening quite often, especially with existing code base that i must modify without a design overhaul. So whether it is a regular method or a table-driven delegate, that still calls for splintering of code blocks which is not always possible.

An example of this would be a series of objects used to do the work and test the condition; if the condition passes, continue to perform rest of work; if it fails no need to continue loop. Breaking this into two method calls in the main loop block would just result in

  1. need to have those series of objects instantiated in the main loop block
  2. passing all the references to both methods
  3. doing the check in the main loop block
  4. in the end, defeats the purpose of refactoring into a single method to make it more logical and readable

Just to check, you are trying to push the break/continue into its own method so that you can avoid returning the enum status?

Top 10 Contributor
Posts 2,284

In a way, yes, although if that was achievable, may affect the readability of the loop pattern abit. One may be lead to think it always does those bits of work when it may be disrupted.

More importantly, I want to know what other developers are doing to simplify their own complicated loops.

The melody of logic will always play out the truth. ~ Narumi Ayumu, Spiral

Top 25 Contributor
Posts 240

I like to split processing into several steps in series when the intermediate results are meaningful. You may end up with a lot of temporary storage and more code, but in some cases this method makes sense.

It would look something like this. Input and output data is passed on from each processing step to the next as Lists but that's just one way of doing it.

FirstWork(List inputdata1A, List inputdata1B...
  out List outputdata1A, out List outdata1B...)
{
  // 1st block of work
  loop (whatever condition)
  {
    if (1st condition)
       break;
    if (2nd condition)
    {
      outdata1.Add(..)
      outdata2.Add(..)
    }
  }
}


Main()
{
  List inputdata1A, inputdata1B;
  List intermed1A, intermed1B;
 
  FirstWork(inputdata1A, inputdata1B,
    out intermed1A, out intermed1B);

  List intermed2A;

  SecondWork(intermed1A, intermed1B,
    out intermed2A);

  List finaloutput;

  ThirdWork(intermed2A,
    out finaloutput);
}

 

 

Page 1 of 1 (12 items) | RSS
Copyright SgDotNet 2004-2008
Powered by Community Server (Commercial Edition), by Telligent Systems