Messy Loop Conditions

Some loops don't have a simple beginning or ending. I wonder what your favorite techniques for avoiding or working around the problems are. An example is the pseudo-code loop under GeneticAlgorithm. Notice how "evaluate each individual's fitness" is in there twice. This is anti-OnceAndOnlyOnce. To fix it we need to jump out in the middle, which ruins top-or-bottom loop conditions, or requires a half-loop.

So make a method IsFit?(anItem), and put the condition there.

It can break up the mental flow in my opinion. Evaluating individuals and the ending condition may not be that closely related. Sometimes there are multiple independent steps before the loop exit test. I don't like creating small functions unless it fixes OnceAndOnlyOnce. I suppose this is a case of that, but it still feels like there should be another approach. I don't like the idea of making another function JUST because it does not fit nicely into loops. (Some believe heavily in lots of small functions/methods. But, I am not one. See LongFunctions.) Maybe I am just over-thinking the issue. But I do encounter it fairly often and have not found a satisfactory solution other than just creating a flag:

  reloop = true;
  while (reloop) {
....
  }
But this may require a bunch of IF statements inside. The general pattern for the problem usually resembles:

  initialize stuff
  begin loop
some complex calculation
if some_condition_based_on_complex_calculation then 
exit loop
end if
primary loop processing
  end loop

A variation is:

  initialize stuff
  begin loop
some complex calculation
if some_condition_based_on_complex_calculation and not first pass then 
  exit loop
end if
primary loop processing
  end loop

It doesn't break flow... it enables it. IsFit?(anItem) is far clearer and easier to read and comprehend than a messy chained together boolean statement with 5 conditions in it. Even it were only used once, I'd break it out to clarify the code and avoid the need for a comment explaining what it does. Programming is all about creating small functions, until you accept that, you'll overthink everything and spend all your time chasing your tail! Using a flag, breaking the loop, jumping somewhere to evaluate, and then re-entering the loop is insane, you're reinventing your own private version of a method call, just use the language and write a method and call it. You're a programmer, methods are your tools, classes are your tools, don't be afraid to use them, that's why they're there.

This appears to be a repeat of the HolyWar in LongFunctionsDiscussion. No need to revisit those here. The bottom line of those debates is that it is a personal preference or dependent on conflicting psychology models. My question is thus addressed to fellow non-short-functioneers.

Maybe, but you're asking about a problem with messy conditions... this is a problem fixed by shorter functions, in fact, this is a problem we don't have because we've learned to learn, and not hang on to bad habits that create silly problems like this. You can't fix long functions, you simply avoid them. The best question is why are you so afraid of a function, it won't bite you know, and you'll feel better later for doing it. And if it doesn't fit the way you think, then fix the way you think, we call that learning, the best solution to a problem is learning how to avoid it in the first place, don't be so stubborn about learning to think differently, that's where all real progress is made.

Re: "And if it doesn't fit the way you think, then fix the way you think": Perhaps it is YOU who thinks wrong. Non-short functions are only messy if you have not learned how to grok them. Two can play this game. Dammit, you dragged me into the "shorty" debate again.

If it were me who thinks wrong, I would be asking for help, but I'm not, I consider this stuff trivial. You are having issues because you are holding onto practices that create them, your ego is getting in the way. Believe me, my ego's gotten in my way many a time, and the outcome is always the same, eventually I learn to set it aside and learn to think differently to get to the next level. Over the years, I've learned to put my ego aside sooner and just learn the new method, it's a far faster way to learn. Use short methods, you won't have any of these problems, in fact, there's a whole list problems that simply disappear with shorter methods, it is a better way. Go read the JavaHotDraw? sources for an example of how clean concise and clear this style of programming is.

{It has nothing to do with my ego. Lots of short functions do not help me grok code. Honest.}

This is a HolyWar; neither of you are going to convince the other of your position. Try refactoring to answer the condition from both points of view. -- AnonymousDonor


I hate to put fuel on this fire but.... When you complain "There must be a better way!", you must be open to a suggestion that "Hey, yes, there is a better way; here it is!".

And, on another note: here's another solution, which depends on some language support of co-routines or tail-calls: each stage in the loop is its own function. The end of each function returns the result of calling the next function. The conditional statement is the only one which could potentially break the loop, by directly returning a value:

 (let-q  //or whichever one allows this type of thing
(
(someComplexCalculation (lambda () (some complex calculation implementation, return (theConditional result)))
(theConditional (lambda (result) (if(result) return blah else return (standardProcessing))
(standardProcessing (lambda () (your standard processing, return (someComplexCalculation)))
)

(initialization) (someComplexCalculation) )

But, we know how much you like small functions... we can only imagine how you'll like this :) -- WilliamUnderwood

Exactly, lambda's are a small function lovers best friend. Top, you got to understand that when you ask for help, you are placing yourself in the position of student, when someone shows you a solution to your problem, learn from it, don't reject it because it's "not the way you think". Obviously its not the way you think, because your thinking can't solve the problem, or you wouldn't be asking. All advanced programming techniques will bend your mind and force you to see things in a new light, but if you don't allow your mind to change the way it thinks, you won't ever learn anything. I've read dozens and dozens of your code samples, and your site, and no offence, but most of the people you have regular discussions with here, are far more advanced programmers than you are, Mr Underwood for example, or Costin, or Doug, etc... so try and learn something from them rather than arguing with them about things they know far more about.

If I get used to the high-fallutin' stuff, then it will be hard to write code that typical companies want. I have already been told that I use too much indirection and too many functions. Remember, companies want plug-compatible developers. Anyhow, how is the above objectively better than breaking out of the loop from the middle? So far it seems like it fits DoTheSimplestThingThatCouldPossiblyWork. "It is better because I am smarter than you" smacks of ArgumentFromAuthority. I believe in the scientific process and open evidence. Perhaps deep-down psychological mind alterations do improve stuff, but if you cannot articulate precisely why, then you also have some homework. Maybe the best programmers are the worse articulators.

Here's the articulation of precisely why: the 'lambda' version precisely describes the state machine, and requires little effort to do it. The flow from each state to the next is completely described by each state. This isn't _necessarily_ a huge win in a simple case, but as you start adding more conditions and operations, the flow of a simple loop becomes nightmarish (status flags, nested conditionals, etc).

Now, one could convert this continuation style (hardcoded in this case) to another form of state machine: a single state variable and a select-case block for instance. Indeed, I might do that if the language didn't support a more direct form of state machine abstraction. What I really want from all this is the ability to look at any one state, and be able to tell what it does, and where it can go from here. Loops with multiple calculations, conditional exits and i/o are notoriously difficult to do that with. The states are intermingled. This isn't a problem with relatively few components (demonstratably: any state I separate out will have some state within itself), but the complexity doesn't scale linearly with the number of components.

Next up, about not being able to write for companies because you can't get away from the high-fallutin concepts. Once you know the concepts, you don't have to have language support in order to use them as an conceptual principle! One can write OO code in C. One can write aspect orientated code in plain java. One can write continuation-passing style in QBasic. By knowing how to convert into different styles of solution, you are free from only being able to view the structure as itself. You can visualize how this state machine would work as completely inline code. You can see how this table is being used as a linked list. And then you write the code in such a way to make this easier, using the concepts and primitives the language provides to describe the concepts and operations that _you_ are creating.

-- WilliamUnderwood

P.S., I'll see if I can dig up a nicely complicated loop to demonstrate with, if you want.

Typical companies don't want code, they want solutions, and all that high-fallutin' stuff allows one to drastically increase one's productivity. Every extra tool you add to you bag of tricks helps you do something cheaper/faster/better. I've seen your code, you hardly use any indirection or functions, so I don't buy that. You're the one who saw something difficult in the genetic algorithm, but that's because you assume that it is implemented inline, you're projecting problems onto it. I see that description as a list of method calls

  ChooseInitialPopulation?();
  EvaluateEachIndividualsFitness?();
  DeterminePopulationsAverageFitness?();
  repeat
  SelectBestRankingIndividual?();
  MatePairsAtRandom?();
  ApplyCrossoverOperator?();
  ApplyMutationOperator?();
  EvaluateEachIndividualsFitness?();
  DeterminePopulationsAverageFitness?();
  until terminating condition (e.g. until at least one individual has 
the desired fitness or enough generations have passed)

''So I don't see any violations of OnceAndOnlyOnce, or any potential problems, because they're just method calls, EvaluateEachIndividualsFitness? is only implemented once, thus no duplication. And the overall algorithm is vastly clearer than if it were implemented inline forcing you to parse all that code mentally. You say you believe in open evidence, but you obviously don't, you only want to justify your current position. If you believed in open evidence, it should be blindly obvious why the above chain of method calls is better than one big function with all the implementation inline. If you can't see that, then you don't really have the capability to be a great programmer.

I guess not. I don't see it. I bow to your greatness. The emperor has spiffy clothes.

You see, I have nothing to prove to you, you're the one asking questions, it's up to you to evaluate the evidence, you'd be wise to try and forget what you think you know when doing so, or you're not really evaluating it. No one is going to offer up any solutions you'll be pleased with, that should make you wonder if it isn't you who needs to learn a thing or two!

Maybe. It is just that I have experience where I change one part and forget to change the matching part that (seems) duplicated above the loop. I usually try to find a way to factor such that I don't have to face such apparent duplication; but if it is good and there for a purpose, then I guess forgetting to change both of the matching parts is my own limitation. However, couldn't anyone use that to justify ignoring OnceAndOnlyOnce? "If you forget to visit each copy, then the fault is you, not factoring."


If the structure of the algorithm is:

  initialize stuff
  begin loop
    some complex calculation
    if some_condition_based_on_complex_calculation then 
       exit loop
    end if
    primary loop processing
  end loop
(as stated above) then why shouldn't that also be the structure of the code? E.g.

  while (true)
  {
    result = ComplexCalculation();
    if (result.finished) 
      break;
    ... primary loop processing ...
  }
I used to feel uneasy when I wrote this, since I felt that writing "while (true)" was not "proper" coding. But, I got over that when I realized that code which matches the underlying algorithm is the easiest to read.

There are many languages which provide a do{}loop structure with no condition built into the loop. The only exit is provided through a break statement. Don't feel bad about while(true) or for(;;) or even t:...:goto t. They have their uses. On another note realize that the pseudo-code given isn't the structure of the algorithm. It is a description of it. -- WilliamUnderwood


ForthLanguage allows for all sorts of loop messiness by allowing multiple WHILE loop exit tests anywhere within a BEGIN-REPEAT or BEGIN-UNTIL loop. You can even specify alternate loop exit paths with ELSE-THEN. Example on HexDumpInManyProgrammingLanguages.


See also LoopingConstructs.


CategoryLoops


EditText of this page (last edited November 3, 2014) or FindPage with title or text search