English version is in beta. All contents of the site should be already translated (mostly using machine translation), and everything should work properly. However, if you find any problems, please contact me.

Program layout and margins

General remarks

Pascal, like many other programming languages, allows for a fairly free design of programs. You can put spaces and line feeds as you like (except, of course, for a number of understandable requirements such as spaces in the expression if a mod b=c then).

Nevertheless, certain rules should be followed — not so that the program is compiled, but so that the program would be easier for a person to read. This is important both in a situation when your program will be read by someone else, and in a situation when your program will be read by yourself. In a well-formatted program, it is easier to find other compilation errors, it is easier to find logical errors in the code, it is easier to add and modify such a program, and so on.

The main purpose of formatting a program is to make its structure better visible, that is, to make loops, if's and other constructions that are important for understanding the sequence of actions better visible. It should be easy to understand which commands are executed in what order in the program, and first of all, which commands relate to which cycles and if's (cycles, if's and similar constructions below I will call control constructions).

Therefore, some rules should be followed. There are many different standards and rules on this topic; in many projects that are written by entire teams of programmers, there are official requirements, and each programmer is obliged to comply with them up to a space. In our classes, I will not be so strict about the design, but nevertheless I will demand compliance with a number of rules (this is some kind of "squeeze", that is common to almost all standards), and I will also strongly recommend following a number of rules.

The sections below will be updated as I remember something or see something in your programs.

Mandatory requirements

  • Use common sense. Any of the rules listed below can be violated if common sense tells you what is better to do wrong — but such situations are the exception rather than the rule.
  • There should be no more than one command and/or control structure on each line.
    • Exception: very closely related commands like assign and reset.
    • Exception: a control structure with only one short command inside, for example:
      if a>0 then inc(i);
      
    • Exception: a for loop with a nested if, which makes sense to "pass only through elements that satisfy the condition":
      for i:=a to b do if x[i]<>0 then begin // there should be no more code here!
      
  • There should be indents in the code — some lines should not be written close to the left edge, but with a few spaces at the beginning:
    if a=0 then begin
        b:=2; // indent in this line
        c:=c+2; // and in this one too
    end;
    
    The basic principle of indentation is that the program can be imagined as a sequence of nested blocks. The main block is the program itself. It can contain simple commands, as well as complex blocks — if's, loops, etc. The code inside an if or inside a loop is a separate block nested in the main block. The code inside the loop inside an if is a block nested in another block nested in a third. Example: the following code:
    read(n);
    for i:=1 to n do begin
        read(a[i]);
        if a[i]>0 then begin
            writeln(a[i]);
            k:=k+1;
        end;
    end;
    if n>0 then
        writeln('!');
    
    the following block structure corresponds:
    +--------------------+
    | main program       |
    | +-----------+      |
    | | for loop  |      |
    | | +-------+ |      |
    | | | if    | |      |
    | | +-------+ |      |
    | +-----------+      |
    | +------+           |
    | | if   |           |
    | +------+           |
    +--------------------+
    
    So, within one block, the indentation should be the same. And for each indoor unit, the indentation should be increased. (In this case, the loop or if header is considered part of the outer block and is written without indentation.)
  • The same can be said in another way: the internal code of the control structure must be indented. If another control structure is embedded in one, then the indentation of the internal code should be doubled, etc. As a result, all commands that are always executed one after the other must be indented (their first characters must go one under the other), and if the order can change somewhere, the indents must be different.
    Stick to the same size of the "base" indentation everywhere in the program, usually 2 or 4 spaces take it. One space is too little.
    Example of margins:
    for i:=1 to n do begin
        read(a); // went inside for --- indentation appeared: 4 spaces
        if a<>0 then begin
            inc(m); // they also entered inside if --- the indentation became twice as large
            b[m]:=a;
        end;
    end;
    for i:=1 to m do
        writeln(b[i]); // if the unit indentation above was 4 spaces, then here, too, 4, not 2!
    
  • The elements indicating the end of part or all of the control structure (else and/or end) must be on separate lines and at the same indentation level as the beginning of the control structure. (This does not apply to begin, because the beginning of the control structure is visible and so.)
    Examples:
    Incorrect:
    for i:=1 to n do begin
        read(a);
        s:=s+a; end; // end is very poorly visible
    if s>2 then
        writeln(s)
        else begin // else is very poorly visible
        writeln('Error');
        halt;
        end; // end is poorly visible
    
    Correct:
    for i:=1 to n do begin
        read(a);
        s:=s+a;
    end; // end is immediately visible
    if s>2 then
        writeln(s)
    else begin // else is immediately visible and breaks the sequence of lines:
        writeln('Error'); // it is clear that these are two branches
        halt;
    end; // it can be seen that the end is there and is not lost
    
    It is allowed to place the phrase end else begin on one line.
  • It happens that you have a whole chain of if constructs that analyzes several cases:
    if dir='North' then
        ...
    else if dir='South' then
        ...
    else if dir='East' then
        ...
    else if dir='West' then
        ...
    else
        writeln('Error!');
    
    According to the meaning of the program, this is a multivariate branching, here all cases are equal or almost equal. The fact that in the program each subsequent if is nested in the previous one is simply a consequence of the fact that there is no way to do multivariate branching in pascal. Therefore, such code should be designed exactly as indicated above, i.e. all branches of else if should be done on one indent. (Not to mention the fact that if you increase the indentation for each such if, the program will go very much to the right.)
    But distinguish this from the following case:
    if a=0 then
        writeln(-1);
    else begin
        if b=0 then
            x:=1;
        else 
            x:=b/a;
        writeln(x);
    end;
    
    Here the options if a=0 and if b=0 are not equal: the option b=0 is explicitly nested inside else.
  • Commands executed sequentially must have the same stumble. Examples:
    Incorrect:
     read(a);
       b:=0;
      c:=0;
    for i:=1 to a do begin
          b:=b+i*i;
        c:=c+i;
     end;
    
    Still wrong (for is always executed after c:=0, so the margins should be the same):
       read(a);
       b:=0;
       c:=0;
    for i:=1 to a do begin 
          b:=b+i*i;
          c:=c+i;
    end;
    
    Correct:
    read(a);
    b:=0;
    c:=0;
    for i:=1 to a do begin 
        b:=b+i*i;
        c:=c+i;
    end;
    
  • You should not unnecessarily move parts of the header of control structures to a new line (conditions in if, while, repeat; assignment in the for header; procedure parameters, etc.). On the other hand, if the header of the control structure turns out to be too long, then you can move it, but then the transferred part should be indented, and in general formatting should be such that it is clearly visible where the header of the control structure ends, and it would be good to highlight the header structure (paired brackets in the condition, etc.)
    Examples:
    Incorrect:
    if
    a=0 then // the condition is short, preferably in one line
    ...
    for
      i:=1
      to 10 do // similarly
    ...
    {too long --- better split}
    if (((sum+min=min2+min3) or (sqrt(sumSqr)<30)) and (abs(set1-set2)+eps>thershold)) or (data[length(s)-i+1]=data[i]) or good then...
    Correct:
    if a=0 then
    ...
    for i:=1 to 10 do
    ...
    {it is clearly visible where the condition ends, plus paired brackets are highlighted}
    if (
          ( (sum+min=min2+min3) or (sqrt(sumSqr)<30) ) and (abs(set1-set2)+eps>thershold)
        ) or (data[length(s)-i+1]=data[i]) or good
    then...
  • In the section var, all lines should be aligned so that the first letter of the first variable in each line would be one under the other; this means that the second and further lines should be indented with 4 spaces. Similarly, in the other sections going up to the code (type, const, etc.), all lines must be equalized by the first character:
    type int=integer;
         float=extended;
    var i:integer;
        s:string;
    
  • Separate procedures/functions from each other and from the main text with an empty line (or two); also use empty lines inside a long program text to break it into logically coherent blocks.

Not so mandatory requirements, but which I strongly recommend following

  • Write begin on the same line as the control structure, or at least on the same indentation as the control structure:
    Very bad:
    for i:=1 to n do
        begin
        read(a[i]);
        ...
    
    Better:
    for i:=1 to n do
    begin
        read(a[i]);
        ...
    
    Even better:
    for i:=1 to n do begin
        read(a[i]);
        ...
    

Example of a well-formatted program:

function sum(a, b: longint): longint;
begin
  sum := a + b;
end;
 
var i, a, b, s: longint;
    x, y: double;
    arr: array [1..1000] of boolean;
 
begin
read(a, b);
 
arr[1] := true;
 
for i := 2 to 1000 do
  if ((a > 0) and (arr[i-1])) then
    arr[i] := true;
 
for i := 1 to 1000 do
  arr[i] := false;
 
s := 0;
if (a < 0) then begin
  a := -a;
  if (b < 0) then begin
    b := -b;
    s := a + b;
  end else begin
    while (s <= 0) do begin
      case a of
        1: begin
          s := s + 3;
        end;
        2: begin
          s := s - 4;
          a := a - 1;
        end;
        else
          s := 1;
      end;
    end;
  end;
end else if (b < 0) then begin
  b := -b;
  s := (a + b) * (a - b);
end else begin
  s := sum(a, b) * sum(a, b);
end;
    
writeln(s);
end.