The Sway Reference Manual/Loops

Sometimes, a programmer wishes to execute a section of code multiple times. This kind of task is commonly performed using a loop. The most basic loop structure in Sway is the while loop, whose format is...

   whileloop: 'while' '(' expr ')' block

where expr is any Sway expression that resolves to the symbols :true or :false.

A while loop tests its condition before the body of the loop is executed. If the initial test fails, the body is not executed at all. For example;

   var i = 10;
   while (i < 10)
       {
       print(i);
       i = i + 1;
       }

never prints out anything since the test immediately fails. In this example, however:

   var i = 0;
   while (i < 10)
       {
       print(i);
       i = i + 1;
       }


the loop prints out the digits 0 through 9:

   0123456789
   

A while loop executes its body (the block portion of the loop) repeatedly (as long as the condition remains true).

To write an infinite loop, use :true as the test expression:

   while (:true)
       {
       var i = getInput();
       process(i);
       }

Loops are function calls

edit

As with if, all Sway loops are function calls, usually with proximal block arguments. If desired, small proximal blocks can be moved inside the parentheses as regular arguments. Also, the semicolon rule for if applies to loops as well: A loop, by itself, having a proximal block argument is not followed by semicolon. If you were to use the return value of a loop (for while, it is :true if the test expression was initially true and :false if the test expression was initially false), you would need to terminate the entire expression with a semicolon.

Other loops

edit

Because loops are so easy to write in Sway, only the while loop is built-in. Other loops can be found in the "basics" library. To use the "basics" library, add the function call:

   include("basics");

at the top of your source code file.

do-until

edit

The do-until loop is similar to the while loop, but the body of the loop is executed once before the test is made. For example:

   var i = 10;
   do-until (i == 10) //note the test
       {
       print(i);
       i = i + 1;
       }

prints out the value of i once:

   10

Note that the do-until runs until the test succeeds, which is the opposite of the while, which runs until the test fails.

The do-until loop is not used very often, though sometimes it is useful for processing interactive input. You can always write a do-until as a while loop. Consider this loop:

   do-until (r == 0)
       {
       r = n / d;
       n = d;
       d = r;
       }

It can be rewritten as a while loop thusly...

   finished = :false;
   while (!finished)
       {
       r = n / d;
       n = d;
       d = r;
       finished = (r == 0);
       }

Note that the variable finished, being initialized to :false, ensures that the body of the loop is entered at least once. At the bottom of the loop, finished is set to the do-until test result.

Usually, the do-until is easier to read and understand.

for loops

edit

Another loop found in many programs is the for loop. The for loop is composed of four major parts:

  • the initialization step
  • the test
  • the body
  • the update

Note that these components correspond exactly to a while loop

   i = 0;                        // initialize
   while (i < MAX)               // test
       {                         // body
       inspect(i);
       i = i + 1;                // update (in body)
       } 

with the update usually being the last statement in the loop body. The corresponding for loop is:

   for (i = 0, i < MAX, i = i + 1)  // init, test, update
       {                            // body
       inspect(i);
       }

See how the update has been moved out of the body.

For loops are commonly used to sweep through each element of an array:

    for (i = 0, i < length(items), i = i + 1)
        {
        a[i] = b[i] * c[i]; 
        }


The for loop is a counting loop. With most, but not all, counting loops, the loop variable (in this case i) is initialized to zero and the loop test (in this case i < length(items)) is configured to succeed as long as the loop variable is strictly less than maximum of the count. If the loop body is to be executed n times, then the loop would look something like:

   for (i = 0, i < n, i = i + 1)
       {
       ...
       }

This convention stems from the fact that arrays in Sway (detailed in a later chapter) use 'zero-based indexing'. That is to say, the first element of an array is located at index zero, the second at index one and so on.

You should always use this convention unless there is good reason not to[1].

for-each loops

edit

The for-each loop is also useful for sweeping through arrays and lists. It does so without explicitly counting, however:

   for-each (s,items)
       {
       inspect(s);
       }

In this loop, each element in items is bound to the variable s, in turn. The loop body executes as many times as there are elements in the array or list. Note how much shorter the for-each loop is compared to the equivalent for loop:

   for (i = 0; i < length(item); i = i + 1)
       {
       var s = items[i];
       inspect(s);
       }

which explicitly counts.

Leaving loops

edit

There are numerous ways to leave a loop, besides having the loop condition or test failing (or succeeding in the case of the do-until). It is possible to return from a function even if in the middle of a loop. This technique is often used when searching an array:

   function find(x,items)
       {
       var s;
       
       for-each(s,items)
           {
           if (x == s)
               {
               return :true;
               }
           }
           
       :false;
       }

Note that if the item isn't found, the loop eventually terminates and failure is noted by returning :false.

You can also leave a loop without leaving the function completely by calling break:

   i = 0;
   while (i < 10)
       {
       if (i > 5) { break(); }
       inspect(i);
       }
   println(:done);

Running this code yields:

   i is 0
   i is 1
   i is 2
   i is 3
   i is 4
   i is 5
   done

You will need to include basics to use break. You could also just throw an exception of type :break instead:

   i = 0;
   while (i < 10)
       {
       if (i > 5) { throw(:break,0); }
       inspect(i);
       }
   println(:done);

Finally, you can leave a loop by leaving the program via the exit function:

   while (1)
       {
       var input = getInput();
       if (reallyBad(input))
           {
           exit(-1);            //non-zero exit code
           }
       }

By convention, Sway programs that exit with code of zero means processing went normally. A non-zero exit code indicates that some sort of fatal error occurred.

Footnotes

edit
  1. Just trust me on this one.


Conditionals · Recursion