2.8 Simple Output Methods

Once the programmer has the means to deal with numbers within programs, it also becomes necessary to print the answers to the screen where they can be seen by the program operator. The sample program module powers in section 2.3 contained the statement

  WriteCard (exponent, 0);

for writing out the value of CARDINAL to the screen. There is also a WriteInt statement and both are, like ReadCard and ReadInt, imported from SWholeIO (Or, in non ISO Modula-2, from InOut). The second number in the WriteInt and WriteCard statements gives the size of the space to write in. Any extra spaces not needed for the number itself are placed to the left of the number. If not enough spaces are provided for, exactly the correct number will be taken. We say that the numbers are right justified in the specified field. That is,

  WriteString ("the number is");
  WriteCard (6, 0);

produces the output

the number is 6

with one space before the 6. (Zero room provided is handled as a special case in ISO standard Modula-2 and a single space is prefixed. In non ISO standard versions, there may be no space at all written in this situation.) The statements

  WriteString ("the number is");
  WriteCard (6, 3);

produce the output

the number is  6

with two spaces before the six, and the statements

  int := -13;
  WriteString ("the number is");
  WriteInt (int, 4);

produce the output

the number is -13

with a single space, because one of the four spaces allowed for is needed for the negative sign. Thus, the second number in WriteCard and WriteInt allow for some formatting of the output number. On the other hand, if one does not care how many spaces are used, the second number in WriteInt or WriteCard should be zero. This was done in the powers example, where the space between the strings and the numerals was created by putting spaces into the strings, rather than having them written by WriteCard. This method is preferred when the number of digits in the answer may vary and works in both ISO and non-ISO libraries.

  (* display the result *)
  WriteCard (base, 0);
  WriteString ( " raised to the power ");
  WriteCard (exponent, 0);
  WriteString (" equals ");
  WriteCard (result, 0);
  WriteLn;

To illustrate, consider a program designed to print out a small table of cardinals (1 through 10) with their squares and cubes. The planning will not be given in detail, but the algorithm used can be expressed in pseudocode as:

  print table headings
  set base to 1
  while base <= 10
    print base
    print square of base
    print cube of base
    increase base by one
  end while

If this algorithm is implemented as:

MODULE SquareCube;
(* by R. Sutcliffe *)

FROM STextIO IMPORT
  WriteString, WriteLn;

FROM SWholeIO IMPORT
  WriteCard;

VAR
  base: CARDINAL;

BEGIN
  (* Write column headings *)
  WriteString ("Number ");
  WriteString ("Square ");
  WriteString ("Cube");
  WriteLn;
  base := 1;
  (*on each line print first, second, and third powers of the base*)
  WHILE base <= 10
    DO
      WriteCard (base, 0);
      WriteCard (base * base, 0);
      WriteCard (base * base * base, 0);
      base := base + 1;
      WriteLn;
    END;

END SquareCube.

the output will look like this:

Number Square Cube
 1 1 1
 2 4 8
 3 9 27
 4 16 64
 5 25 125
 6 36 216
 7 49 343
 8 64 512
 9 81 729
 10 100 1000

because no spacing has been included. (In many non ISO versions, the numbers will be jammed together with no spaces at all, but the ISO standard requires one space to be output before the number in the special case that the room allowed is zero.) Putting one extra space into the WriteCard statements is not sufficient, for only the one digit numbers will then be properly separated. Instead, the program should lay out a well-spaced table. Moreover, this is a good place to use a constant, for the upper limit of the table may well be subject to change. If it is re-written as:

MODULE SquareCubeFormatted;
(* by R. Sutcliffe *)

FROM STextIO IMPORT
  WriteString, WriteLn;

FROM SWholeIO IMPORT
  WriteCard;

CONST
  maxToDo = 10;

VAR
  base: CARDINAL;

BEGIN
  (* write column headings *)
  WriteString ("         Number");  (* allow 15 spaces for each *)
  WriteString ("         Square");
  WriteString ("           Cube");
  WriteLn;
  base := 1;
  (* on each line print first, second, and third power of base *)
  WHILE base <= maxToDo
    DO
      WriteCard (base, 15);
      WriteCard (base * base, 15);
      WriteCard (base * base * base, 15);
      base := base + 1;
      WriteLn;
    END;

END SquareCubeFormatted.

the output is

         Number         Square           Cube
              1              1              1
              2              4              8
              3              9             27
              4             16             64
              5             25            125
              6             36            216
              7             49            343
              8             64            512
              9             81            729
             10            100           1000

There is also a procedure in STextIO (or in InOut) to output CHAR variables. This is the WriteChar statement (called Write when found in InOut,) and it too can output either literal characters, or those designated by constant or variable names. Here is a brief example:

VAR
  ch : CHAR;

BEGIN
  ch := 'R';
  WriteChar (ch);
  WriteChar ('.');
  WriteChar ('S');
  WriteChar ('.');  (* outputs my initials *)

and, another:

MODULE GetIt;
(* by R. Sutcliffe *)
(* prints out a little joke *)

FROM STextIO IMPORT
  WriteString, WriteLn, WriteChar;

FROM SWholeIO IMPORT
  WriteCard;

CONST
  wye = "Y"; (* Character constants are allowed. *)
  four = 4;

BEGIN
  WriteChar (wye);
  WriteChar (wye);
  WriteString ("UR ");
  WriteChar (wye);
  WriteChar (wye);
  WriteString ("UB ");
  WriteChar ("I");
  WriteChar ("C");
  WriteString (" UR ");
  WriteChar (wye);
  WriteChar (wye);
  WriteCard (four, 2);
  WriteString (" ME!");
  WriteLn;
END GetIt.

the output of which is

YYUR YYUB IC UR YY 4 ME!

Contents