OML for the complete beginner, Lesson 8

Loops

A loop is exactly that--when the computer reaches the "end" of the loop, it loops back around to the beginning of the loop and starts over. Some loops control how many times the computer will loop (some of these can even change how many times they loop, as needed), and some have no control whatsoever. Loops are basically another form of program flow control.

The most basic loop involves the Goto statement, which you were introduced to last time.

(Warning: Do not try the following mini-macro without taking proper precautions! Unless you use the green arrow on the Macro Editor toolbar to run this macro (in which case you should be able to stop it at any time by using the stop button on the macro editor toolbar), it will cause Connexion to freeze or crash!)

  Sub Main
    Dim CS as Object
    Set CS = CreateObject("Connex.Client")
 
    x = 1
    Text$ = "999  Hello, world " & Str$(x)
    bool = CS.AddField(1, Text$)

  Start:
    Text$ = "999  Hello, world " & Str$(x)
    CS.SetField(1, Text$)
    x = x + 1
    Goto Start
  End Sub

This macro will theoretically run forever--in practice, it will run until the program locks up or the user halts the macro--endlessly finding the first 999 line in a record and putting the text "999    Hello, world" there and a number representing the number of times the loop has executed.

Labels are usually a word or combination of words written without spaces or with underscores instead of spaces, that are the first or only thing found on a line and end with a colon. When referred to by other commands, such as Goto, the colon is left off. Labels by themselves can also be used to identify various parts of a program if you don't need to use long and involved commented explanations; this is one way to tell at a glance what a certain program block does.

This macro provides us with the most basic example of a loop--in this case, an infinite loop. When the computer reaches the Goto statement, it loops back around to the line marked "Start" and proceeds to execute each of the commands found there. Since there is no way to determine when to stop looping, it will execute an infinite number of times. However, infinite loops are extremely bad things, to be avoided at all costs outside of the classroom. It's not too difficult for an infinite loop to eat up so much of the computing resources available that Connexion crashes and has to be closed down and restarted! (If there's one thing Connexion and Microsoft Windows both loathe, it is infinite loops.)

If you need a loop to execute a series of commands a specific number of times, one method is to use a For...Next loop.

  Sub Main
    Dim CS as Object
    Set CS = CreateObject("Connex.Client")

    For i = 1 to 10
      CS.GetFieldLine(i, field$)
      CS.SetFieldLine(i, field$ & " Hi there!")
    Next i
  End Sub

This program puts the words "Hi there!" at the end of each of the first ten lines of an active record. Note that it is optional to use the i with the Next statement, but it can help to keep your loops separate in your mind when you combine them:

  Sub Main
    Dim CS as Object
    Set CS = CreateObject("Connex.Client")
 
    x = 9
    For i = 1 to x Step 2
      For j = 1 to 3
        CS.GetFieldLine(i, field$)
        CS.SetFieldLine(i, field$ & " " & Str(j))
      Next j
    Next i
  End Sub

This program appends the numbers " 1 2 3" one by one to the end of every odd numbered line up to and including line 9. The Step part of the command tells it, in this case, to count from 1 to 9 by two's.

A For...Next loop is okay for some things, but it is only of any real use when you know in advance exactly how many times the loop must execute. It also can have serious problems if you use variables, such as the x in the above example, for anything but counting (i and j in the above example). If you redefine the counting variables with statements inside the loop, it is extremely easy to end up with a macro that simply doesn't work or, more likely, an infinite loop. As you can see, while it does have its uses, the For...Next loop isn't really all that versatile, especially when you consider its cousin, the Do...Loop statements.

  Sub Main
    x = 0
    Do Until x >= 10
      x = x + 1
    Loop

    y = 0
    Do
      y = y + 1
    Loop Until y >= 10

    z = 0
    Do While z < 10
      z = z + 1
    Loop
  End Sub

These three loops seem to be functionally the same, but upon closer examination, some important differences come to light. The first one hits the Do statement, sees that x is less than 10, and starts executing the commands inside the loop until it does equal 10. It will execute 10 times, then skip from the Do statement to the first statement after the loop. The second goes through the entire loop once before testing whether or not to loop, at which point y will already equal 1; therefore it will only run 9 times. Also, if x = 10 and y = 9, all of the commands inside the first loop would be skipped, but all of the commands inside the second loop would execute once. The third is an alternate wording that works better in conjunction with certain other commands rather than a simple inequality, such as Do While InStr(1, a$, b$), which will loop for as long as b$ can be found somewhere within a$.

Like all loops, Do...Loops have the unfortunate tendency to easily result in infinite loops. Here is an easy mistake to make:

  x = 11
  Do Until x = 10
    x = x + 1
  Loop

Since x starts out larger than 10 and the loop only makes it bigger yet, x can never equal 10. Thus, this is an infinite loop.

However, these loops also allow a surefire method of preventing this from happening. If you think an infinite loop might be a possibility, it is best to add in an independent counting variable that does nothing but tally how many times the loop has executed.

  Sub Main
    x = 0 : y = 0
    Do Until x = 11
      If y >= 20 Then
        Exit Do
      End If
      x = x + 2
      y = y + 1
    Loop
    MsgBox "I made it out of the loop."
  End Sub

As you can see, this loop has a problem: since it is counting x from zero by twos, it will successively equal 2, 4, 6, and so forth, and will never equal eleven. If that were all there was to the loop, the loop would go on and on, endlessly waiting for an eleven to turn up. However, we have added another variable, a "control group", as it were, to prevent that from happening. The loop statements themselves are only concerned with the value of x; however, each time the program loops, y also increases by one. When y reaches 20--that is, when the program has looped 20 times--then an Exit Do statement is executed, which automatically sends the program on to the command immediately following the Loop statement. In this case, a message box pops up showing that the program made it beyond the loop.


Now that you understand basic program flow and loops, you're ready for one of the more fun tricks in writing Connexion macros: writing a macro that can affect each of the records in a list, such as your online save file.

  Sub Main
    Dim CS as Object
    Set CS = CreateObject("Connexion.Client")

    WindowType% = CS.ItemType
    If WindowType% <> 7 And WindowType% <> 21 Then
      MsgBox "Not viewing a bibliographic save file list.  Exiting..."
      Goto Done
    End If

    bool = CS.GetFirstItem
    NextRec = TRUE
    Do
      bool = CS.AddField(99, "590  Macro-edited record.")
      bool = CS.SetMyStatus("Macro-edited record")
      bool = CS.SaveRecord
      NextRec = CS.GetNextItem
    Loop While NextRec <> FALSE
    bool = CS.CloseRecord(TRUE)

  Done:
  End Sub

The first section of code obviously just sets up the CS shortcut. After that is a section that makes sure that there is either an online or a local save file list for the macro to process. (Or rather, if the active window is not an online save file list or a local save file list, it tells you so and quits.) Following that is the meat of this macro, including two new commands.

The first line of that section opens up the first record in the list. The second sets up a variable that will be used to test whether there's another record left to process. After that is the loop. Inside the loop are four commands. The first three represent whatever you want the macro to do to each record; in this case, they simply add a field, change the "MyStatus" text, and save the record. The fourth command attempts to go forward to the next record. If there is another record to go to, it closes the current record, opens the next, and returns to the top of the loop. If there isn't another record, it ends the loop, leaves the current record open, and moves on with the macro. (Note that, due to some irregularities in the way TRUE works, it is better to say "not equal to FALSE," as I did here, than say "equal to TRUE.") When the loop is done and the last record has been affected and the loop has ended, the macro cleans up after itself and returns things to the way it found them--it closes the active record, which returns you to the save file list. Now that's a powerful macro!


Speaking of power, what if you don't want a macro to process every record in a list, but rather only those records you've selected? That's possible, too! Here is the same macro as above, except that it only opens records that you have first selected, either by selecting one line on the list then pressing Shift or Control and clicking another line, or if a CS.SearchList command finds and automatically selects several records.

  Sub Main
    Dim CS as Object
    Set CS = CreateObject("Connexion.Client")

    WindowType% = CS.ItemType
    If WindowType% <> 7 And WindowType% <> 21 Then
      MsgBox "Not viewing a bibliographic save file list.  Exiting..."
      Goto Done
    End If

    bool = CS.GetFirstSelectedItem
    NextRec = TRUE
    Do
      bool = CS.AddField(99, "590  Macro-edited record.")
      bool = CS.SetMyStatus("Macro-edited record")
      bool = CS.SaveRecord
      NextRec = CS.GetNextSelectedItem
    Loop While NextRec <> FALSE
    bool = CS.CloseRecord(TRUE)

  Done:
  End Sub

Again, the first two sections set up the CS shortcut and make sure that the type of data the macro expects is displayed on the screen. Then comes the loop, with all of the same commands except that CS.GetFirstItem and CS.GetNextItem have been replaced with CS.GetFirstSelectedItem and CS.GetNextSelectedItem, respectively. These commands work as you might expect (and exactly as if you had selected several records, then opened the first selected item, then clicked the Forward button to jump directly to the next selected item.

With these commands at your fingertips, automatically making certain changes to a whole bunch of records just got easier!

Next time, subroutines and functions...


Return to Lesson #7.
Return to Main page.

Copyright 2004, Joel A. Hahn
Sponsored and endorsed by OCLC Online Computer Library Center, Inc.