HOME  →  Lazarus Tutorials TOC     Other material for programmers
Delicious.Com Bookmark this on Delicious     StumbleUpon.Com Recommend to StumbleUpon

Foot note to

"The 'fall-through loop': A useful structure

This page has good information, and a search button at the bottom of the page
Please don't dismiss it because it isn't full of graphics, scripts, cookies, etc!

This page is "browser friendly". Make your browser window as wide as you want it. The text will flow nicely for you. It is easier to read in a narrow window. With most browsers, pressing plus, minus or zero while the control key (ctrl) is held down will change the texts size. (Enlarge, reduce, restore to default, respectively.) (This is more fully explained, and there's another tip, at my Power Browsing page.)


This essay is a footnote to my longer tutorial about what I'm calling the "fall through" loop. Skim the page you are on, by all means, it may make sense by itself. But if you find it heavy going, or if you want to know more, try the page where I introduce the "fall-through" loop.

The discussion is in pretty general terms, but from the point of view of a Delphi or Lazarus programmer. There is nothing to stop you from using the same ideas in other languages, though.



The problem solved by the Fall-Through Loop

"Fall-though" loops are useful when you want the computer to repeatedly perform a series of tasks. A typical application is a program to monitor a number of electronic sensors, e.g. weather sensors, and record the readings.

Here, in pseudo-code form, is a simple program using a fall-through loop....

InitializeStuff;// the following bits of initialization
    //would probably be inside "InitializeStuff", but
    //are show "broken out" to make them more visible
    //for the purposed of the tutorial

   iWhichTaskMax:=60;//Value in iWhichTaskMax doesn't change
   iWhichTask:=0;

Repeat

  if iWhichTask=10 then ReadAndDealWithIndoorTture;
  if iWhichTask=20 then ReadAndDealWithOutdoorTture;
  if iWhichTask=40 then ReadAndDealWithBarometer;

  iWhichTask:=iWhichTask+1;
  if iWhichTask>iWhichTaskMax then iWhichTask:=0;

  application.processmessages;

Until false;

Each line above (apart from the "Repeat" and "Until false" is just a call to a subroutine I would have to supply, to build the program this way. (In the parent page of the page you are reading, we created a fall through loop which was a little more clever, but the essence is in what you see above.)

By "ReadAndDealWith..." I mean to indicate that the subroutine would read the sensor, and take care of updating the display which the weather watching program creates. (My program also records the data it collects to a machine readable file.)

So far, so good.

But sometimes, when you come to do, say "ReadAndDealWithIndoorTture", it is useful to know what reading you saw the previous time you read the indoor temperature.

And, in a similar, but even more useful way, it is sometimes useful to know the time of day when the loop most previously reached the "application.processmessages" line. We'll talk more about that in a moment.

All we need to do is to add two variables, and initialize them sensibly, and keep revising their contents.

Even if we weren't going to have a variable for the temperature (tture) at the time we previously measured it, it would make sense to have a variable to hold "the number" returned by the tture when we call whatever subroutine actually takes the reading. We'll assume that the tture can be held in an integer variable (tture expressed in centi-degrees Celsius, perhaps? i.e. body tture about 3700 centi-degrees. (Integer data is so much easier to deal with!)... so, our variable for indoor- temperature- just- read might be called iTtureIndoor.

In the case of the time, I know that the loop will always execute in less than 60 seconds, and will rarely take exactly 60 seconds. I also only care whether more than a full second has elapsed since we last came to "application.processmessages". Whew! You'll see why all those caveats help in a moment.... so, we will have a variable for seconds- past- the- minute- right- now. We will call it bTimeSecsPart.

Right!

Now I'm ready to give names to the two new variables I proposed a while ago....

They can be...

Inside ReadAndDealWithIndoorTture we will be able to look in iTtureIndoorPrev to see what the tture was the last time we checked it. Perhaps we want to display the tture in figures, in red if the tture is rising, in blue if it is falling? Right at the end of ReadAndDealWithIndoorTture we will do a....

iTtureIndoorPrev:=iTtureIndoor;

... to prepare the way for the next time we enter ReadAndDealWithIndoorTture.

We will need to give iTtureIndoorPrev some value before the "repeat" which starts the fall-though loop. I can't think of anything you might want to do with the difference between the "previous" reading and the current reading, the first time you go though the loop when there is (probably) no previous "real" reading. If what you are doing based on that difference matters, you could arrange to read the tture once before entering the loop. But those little, extra, "special" visits to sensors can sometimes lead to problems. Best, in this case, probably, just, before the "repeat" to set iTtureIndoorPrev to some "reasonable" value, and know that whatever comes out of the inspection of the difference between "now" and "previously, the first time ReadAndDealWithIndoorTture is executed won't be terribly meaningful.

So far...

So far, we have thought about a mechanism for keeping track of what some parameters were on the previous execution of the subroutine where they are sensed. We have "dealt with" everything relating to using the previous tture of a particular sensor.

Now I want to talk a bit more about why I proposed the bTimeSecsPartPrev variable.

The time it takes to go from "repeat" to "until" in the fall-through loop will be variable. For all sorts of reasons, don't even think about trying to make it execute at a steady rate.

However, the program is being written for the use of humans, and humans often need to relate to seconds, minutes, hours. The program should do so as well. But, with the loop executing "slowly" (in computer terms!) sometimes, "quickly" others, obviously, there is no easy way to relate "trips through loop" to "seconds which have elapsed".

But Baldrick has a cunning plan.

For the sake of illustration, let's say that we want to display the current time of day on the screen as part of what the program displays. (Well... the time of day according to the computer, anyway.)

We could simply insert the following just before the "application.processmessages" inside the loop....

ReadTimeOfDay;
PutTimeOfDayOnScreen;

But that would be terribly inefficient, inelegant. Ick.

So, let's do this....

Before the "repeat", we will read the time of day, and put it on the screen... hours, minutes, seconds. We will put the "seconds past the most recent full minute" at that time in bTimeSecsPartPrev.

Then, just before the "application.processmessages" inside the loop, we'll put....

ReadTimeOfDay;
bTimeSecsPart:=(the "seconds" part of the current time);
if bTimeSecsPartPrev<>bTimeSecsPart then begin
     PutTimeOfDayOnScreen;//(to update the previously displayed time)
     bTimeSecsPartPrev:=bTimeSecsPart;
     end;

NOW we only update the time shown on the screen when it is necessary. We aren't repeatedly doing that even when the time hasn't changed.

Why bother?

If the savings arising from not repeatedly re-displaying something which hasn't changed seem unimportant to you, consider the usefulness of something else which can be accomplished with similar code.

Remember that we are building all of this on the premise that one trip through the loop can't take more than a second.

Suppose, say, you have a graph on the display. And you want it to scroll every 30 seconds.

You don't know how long one pass from "repeat" to "until" will take, remember, and also that very brief interval is variable, anyway.

The following, which isn't so very different from what we had before, uses again ideas we've already discussed, accomplishes that... the routine "ScrollGraph" will be called once every thirty second. The main "repeat/until" loop may have executed 40 times or 400 times... but the scroll will happen only once every 30 seconds.

InitializeStuff;// the following bits of initialization
    //would probably be inside "InitializeStuff", but
    //are show "broken out" to make them more visible
    //for the purposed of the tutorial

   iWhichTaskMax:=60;//Value in iWhichTaskMax doesn't change
   iWhichTask:=0;

   bSecsSinceScrollToDoIt:=30;//value doesn't change
   bSecsSinceScroll:=0;

   bTimeSecsPartPrev:=(the "seconds" part of the current time);

Repeat

  if iWhichTask=10 then ReadAndDealWithIndoorTture;
  if iWhichTask=20 then ReadAndDealWithOutdoorTture;
  if iWhichTask=40 then ReadAndDealWithBarometer;

  iWhichTask:=iWhichTask+1;
  if iWhichTask>iWhichTaskMax then iWhichTask:=0;

  ReadTimeOfDay;
  bTimeSecsPart:=(the "seconds" part of the current time);
  if bTimeSecsPartPrev<>bTimeSecsPart then begin
       PutTimeOfDayOnScreen;//(to update the previously displayed time)
       bTimeSecsPartPrev:=bTimeSecsPart;
       bSecsSinceScroll:=bSecsSinceScroll+1;
       if bSecsSinceScroll>=bSecsSinceScrollToDoIt then begin
          ScrollGraph;
          bSecsSinceScroll:=0;
          end;
     end;

  application.processmessages;

Until false;

Ta da! We're "done"!

For extra credit

What if... one pass through the "repeat/until" loop can take more than a second?

Let's say it can take up to five seconds, because one of the subroutines which executes from time to time (when the value in iWhichTask makes it that subroutine's "turn") can take longer than a second. Sigh.

Mostly, the program we have already will still work. But the scrolling will now only occur aboutevery thirty seconds.

And "fixing" that will be a little tedious, because suppose the previous "seconds" figure was 59, and three seconds have passed. Now the "seconds" figure is 02.

The following code does a better job. Now the scroll will occur the next time after 29 seconds that the "seconds" figure goes up. It may be at 30 seconds, or 30 and a few seconds... but at least we won't be throwing away "extra" seconds all through the "waiting for "30" to pass" process...

Most of the code is as before. The changes are all in this block...

  ReadTimeOfDay;
  bTimeSecsPart:=(the "seconds" part of the current time);
  if bTimeSecsPartPrev<>bTimeSecsPart then begin
       PutTimeOfDayOnScreen;//(to update the previously displayed time)

       bTmp:=bTimeSecsPart;
       if bTimeSecsPartPrev>bTimeSecsPart then bTmp:=bTmp+60;
       bTmp:=bTmp-bTimeSecsPartPrev;

       bTimeSecsPartPrev:=bTimeSecsPart;

       bSecsSinceScroll:=bSecsSinceScroll+bTmp;
       if bSecsSinceScroll>=bSecsSinceScrollToDoIt then begin
          ScrollGraph;
          bSecsSinceScroll:=0;
          end;

Done!

I hope it helps you with your programming wants. Comments welcome, there's "how to contact me" just a little farther down the page.





Search across all my sites with the Google search...

Custom Search

Or search just SheepdogGuides.com with the Freefind tool...

   Search this site or the web          powered by FreeFind
  Site search Web search
Site Map    What's New    Search

This search merely looks for the words you enter. It won't answer "Where can I download InpOut32?"


If you visit 1&1's site from here, it helps me. They host my website, and I wouldn't put this link up for them if I wasn't happy with their service. They offer things for the beginner and the corporation.www.1and1.com icon

Ad from page's editor: Yes.. I do enjoy compiling these things for you. I hope they are helpful. However... this doesn't pay my bills!!! Sheepdog Software (tm) is supposed to help do that, so if you found this stuff useful, (and you run a Windows or MS-DOS PC) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!
Click here to visit editor's freeware, shareware page.

Link to Lazarus Tutorials main page
How to contact the editor of this page, Tom Boyd


Please consider contributing to the author of this site... and if you don't want to do that, at least check out his introduction to the new micro-donations system Flattr.htm....


Valid HTML 4.01 Transitional Page tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org. Mostly passes. There were two "unknown attributes" in Google+ button code. Sigh.


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....