5 Multiplication Table

In this section, we will take on the simple task of printing out a multiplication table of the integers from 1 to 9. For the purpose of illustration we shall introduce the following concepts:

for printing in fixed width columns. We shall also show the correct BETA way to do formatted output, involving introducing an extension to a virtual pattern.

5.1 The for imperative

In BETA, all syntactic structures have a similar form of delimiters with opening and closing parentheses and the for is no exception:

Block:  (#   ...   #)
Comment:        (*   ...   *)
For:    (for ... for)

In opposition to other languages where the for can count up or down and the step can be varied, in BETA only a simple version exists going from 1 to N where N is an expression (or evaluation to use the BETA terminology). This follows the BETA minimalist philosophy of providing the strict minimum combined with powerful extension mechanisms.

More precisely, there are two forms of the for, depending on whether one wants to have access to the counting variable or not. These are:

(for <evaluation> repeat <imperatives> for)

and

(for <var> : <evaluation> repeat <imperatives> for)

Both forms will be used in this section. Note that the loop variable <var> acts as a locally defined variable and is only accessible inside the for imperative. Thus the loop variable <var> need not be declared elsewhere.

Neglecting labels, the body of our program could have the following form:

(for i:9 repeat
    (for j:9 repeat
         i*j -> putint;
         2 -> tab;
    for);
    newline;
 for);

Where tab is a procedure that outputs blanks to separate the integers printed with putint. In contrast with previous versions of tab which printed a fixed number of blanks, here we provide it with a parameter N to indicate the number of spaces to write. Above, in 2 -> tab, we want to print out 2 spaces.

In BETA, parameters are considered to be local variables which are assigned values from an input list before executing the procedure body. Hence they are declared the same way as any other local variable. The number of actual parameters that must be supplied and which local variables will receive these values is specified by an enter list. tab can be defined as follows:

tab: (# N: @integer
     enter N
     do (for N repeat ' ' -> put for)
     #);

The enter list comes after the declarations and before the do part.

Here is the program:

Program 5: Multiplication1.bet

ORIGIN '~beta/basiclib/betaenv'
---- program: descriptor ----
(# 
   (* Multiplication Table
    *
    *  Objectives: 
    *    - use the FOR imperative
    *    - introduce a parameterized procedure
    *************************************************)
   
   tab: (# N: @integer;
        enter N
        do (for N repeat ' '->put for)
        #);
   
do '\n\t** Multiplication Table ** \n\n'->puttext;
   4->tab;
   (for i: 9 repeat
        i->putint; 2->tab;
   for);
   newline; newline;
   (for i: 9 repeat
        i->Putint; 3->tab;  
        (for j: 9 repeat
             i*j->putint ;
             2->tab;
        for);
        newline
   for)
#)

Since this program uses no reals, we have used the same simplified fragment statements that we used in our first program. The results are shown below:

** Multiplication Table ** 

    1  2  3  4  5  6  7  8  9  

1   1  2  3  4  5  6  7  8  9  
2   2  4  6  8  10  12  14  16  18  
3   3  6  9  12  15  18  21  24  27  
4   4  8  12  16  20  24  28  32  36  
5   5  10  15  20  25  30  35  40  45  
6   6  12  18  24  30  36  42  48  54  
7   7  14  21  28  35  42  49  56  63  
8   8  16  24  32  40  48  56  64  72  
9   9  18  27  36  45  54  63  72  81

In this output, the columns do not line up because some of the integers use require a single digit and others require 2 and putint prints with minimum space.

To overcome this, we will write a simple procedure to output an integer in a field of width w. By simple, we mean that it will only work with positive integers less than 1000. To determine the number of digits required to print an integer we use the if imperative. The if imperative has two forms:

(if <Exp> then
    <Imperatives>
 else
    <Imperatives>
if);

and

(if <Exp> 
    // <Exp> then <Imperatives>
    // <Exp> then <Imperatives>
    // <Exp> then <Imperatives>
    ...  
 else
    <Imperatives>
if);

A first crack at the code to compute ND the number of digits in an integer i is:

(if true
 // i < 10  then 1 -> ND
 // i < 100 then 2 -> ND
            else 3 -> ND
if)

This is coded along the lines of the standard if...else if...else if... pattern of other languages such as Pascal or Simula but is not quite correct according to the strict definition of the if imperative, because the if does not specify that the alternatives will be evaluated in a sequential manner. Therefore if I= 5, ND could receive either 1 or 2 as a value. Although the code would probably work, it should be written as:

(if true
 //              i<10   then 1 -> ND
 // (i>=10) and (i<100) then 2 -> ND
                        else 3 -> ND
if)

Remember that the parentheses are required in the second alternative due to the operator priorities.

Now we can design a procedure, (N,W) -> Outint, that will print an integer N right justified in a field of W spaces wide. This procedure will require a list of 2 parameters when it is called.

Outint: 
  (# N,W: @integer;
  enter (N,W)
  do (if true
      //              N<10   then 1 -> ND
      // (N>=10) and (N<100) then 2 -> ND
                             else 3 -> ND
      if);
      ND -> tab;
      N -> putint;
  #);

The complete modified program is given below and the output follows.

Program 6: Multiplication2.bet

ORIGIN '~beta/basiclib/betaenv'
---- program: descriptor ----
(# 
   (* Multiplication Table 2
    *
    *  Objectives: 
    *    - use the IF and FOR imperatives
    *    - use procedures & parameters
    *************************************************)
   
   tab: (# N: @integer;
        enter N
        do (for N repeat ' '->Put for)
        #);
   
   Outint: 
     (# N,W, ND: @integer;
     enter (N,W)
     do (if true
         //N<10   then 1->ND
         //(N>=10) and (N<100) then 2->ND
         else 3->ND
        if);
        W-ND->tab;
        N->putint;
     #);
   
do
   '\n\t** Multiplication Table ** \n\n'->puttext;
   
   4->tab;
   (for i: 10 repeat
        (i,3)->Outint;
   for);
   newline; newline;
   
   (for i: 10  repeat
        (i,4)->Outint;
        (for j: 10 repeat
             (i*j,4)->Outint;
        for);
        newline
   for)
#)

and the results:

** Multiplication Table ** 

       1   2   3   4   5   6   7   8   9  10

   1   1   2   3   4   5   6   7   8   9  10
   2   2   4   6   8  10  12  14  16  18  20
   3   3   6   9  12  15  18  21  24  27  30
   4   4   8  12  16  20  24  28  32  36  40
   5   5  10  15  20  25  30  35  40  45  50
   6   6  12  18  24  30  36  42  48  54  60
   7   7  14  21  28  35  42  49  56  63  70
   8   8  16  24  32  40  48  56  64  72  80
   9   9  18  27  36  45  54  63  72  81  90
  10  10  20  30  40  50  60  70  80  90 100

Finally, the way formatted output is done in the demo programs distributed with the Mjølner System. It involves extending the virtual pattern format defined in putint. Thus the OutInt procedure of Program 4.1 should be rewritten as follows:

Outint:
  (# N,W : @integer;
  enter (N,W)
  do N -> screen.putint(# format::(# do W -> width #)#)
  #);

and the results would be identical to those obtained previously. The latter procedure would be much more robust, accepting any positive or negative integer value. Program Multiplication3.bet uses this procedure. See also Section 12.1.


Libraries Tutorial
© 1994-2004 Mjølner Informatics
[Modified: Thursday January 16th 2003 at 10:23]