The Pascal Cycles

while - do

in the form:

while <condition> do <command>;

Behavior: The <condition> is evaluated and if the result is True, then the <command> is done, then repeats from the condition evaluation again (till False).

Note: if the <condition> is not evaluated as the True as a first, than not any <command> is done.

If we need to execute more commands inside the cycle, we can use the program brackets (begin and end). In the Pascal, every where, where can be a command, can be the begin keyword, then as many commands, as we need, separated by semicolon, then the end keyword.

repeat - until

in the form:

repeat
 <command>;
  <command>;
  ...
  <command>;
until <condition>;

No testing before the until keyword. If program goes here, the <condition> is evaluated; if it is not positive (the result is False), then program returns to the repeat keyword. In any case, the commands between repeat and until will be done at least once.

Example: count, how many spaces is in the sentence (in the Edit1 component).

Solution: We should make a copy of the string to local variable and prepare zero in another local variable. After a space is being counted in, we can erase it.

There can be no space, so the while - do cycle is the best solution:

procedure Button1Click(Sender:TObject);
  var
    i : integer;
    s : string;
  begin
    i:=0;
    s:=Edit1.Text;
    while pos(' ',s)>0 do 
      begin
        i:=i+1;
        delete(s,pos(' ',s),1);
      end;
    Button1.Caption:=IntToStr(i);
  end;

We have two commands inside the cycle, so the begin and end brackets are needed. Typical beginner's error is not to write the appropriate end.

This is just a school example - in this case, the for - do cycle will be faster (because of famous speed of the Borland products, there are better to write a function, then call it). We don't need to delete any space (slow), we can just go through the string and if we meet a space, count it in. The faster solution:

procedure Button1Click(Sender:TObject);
  var
    i,j : integer;
    s : string;
  begin
    i:=0;
    s:=Edit1.Text;
    for j:=1 to length(s) do
      if s[j]=' ' then i:=i+1;
    Button1.Caption:=IntToStr(i);
  end;

But the first solution can be still used, if you like to count a longer strings (few words?). For example, in the Edit1 there are this text: "Can you can a can as a canner can can a can?". In the Edit2 is the word "can" - we should to count, how many times is this in the longer text.

Possible solution:

procedure Button1Click(Sender:TObject);
  var
    i : integer;
    s,a : string;
  begin
    i:=0;
    s:=Edit1.Text;
    a:=Edit2.Text;
    while pos(a,s)>0 do 
      begin
        i:=i+1;
        delete(s,pos(a,s),length(a));
      end;
    Button1.Caption:=IntToStr(i);
  end;

In this case, the for-do cycle would be still faster, but much more complicated.

Another example:

//cycle for all numbers from 0 to 11 with the step of 0,375:
x:=0;
while x<=11 do
  begin

 {some commands...}

    x:=x+0.375;
  end;

Example: The sequence is defined by xi+1 = (xi*7+1) mod 11 and the first number = 1. The question is, how many members of this sequence have to be summed, so the result is bigger then 100 ?

For solving this, we need a place to present the result. It could be a Label; we can use the same component to execute our procedure (see the next example), or you can modifi it and write result to the Button caption (property):

procedure TForm1.Label1Click(Sender: TObject);
  var
    x,n,sum: integer;
begin
  x := 1;
  sum := 0;
  n := 0;
  while sum<100 do
    begin
      sum := sum + x;
      n := n+1;
      x := (x*7+1) mod 11;
    end;
  Label1.Caption:='There are '
    + IntToStr(n)
     + ' numbers from the sequence to get the sum of '+IntToStr(sum);
end;

The result is 19. If we like to see them, we can add a Memo and write the individual numbers here (don't forget to erase it before start of the cycle):

procedure TForm1.Label1Click(Sender: TObject);
  var
    x,n,sum: integer;
begin
  x := 1;
  sum := 0;
  n := 0;
  Memo1.Lines.Clear;
  while sum<100 do
    begin
      sum := sum + x;
      n := n+1;
      Memo1.Lines.Add(
        IntToStr(n)
         +'. = '
          +IntToStr(x));
      x := (x*7) mod 11;
    end;
  Label1.Caption:='There are '
    + IntToStr(n)
     + ' numbers from the sequence to be summed.';
  Memo1.Lines.Add('The resulting sum is : '+IntToStr(sum)+'.');
end;

It is important to increment the n value before adding the result to Memo1, because we need the correct value and to get the correct result, we have to count from zero.

Added lines (changes against previous example) are in black, the older one gray.

Do not forget the vertical scrollbar for the Memo1, or make it very long (20 lines).

For an individual work:

Read some text file to the memo and count, how many letters "a" it contain (uppercase/lowercase?).

Sorting: bubble - sort

The easiest method of sorting. So simple, that programmers are used to write it, instead to look for it somewhere on the internet. But slow.

The algorithm: we go through the array, comparing each pair of the values (on the i position and the next, on the i+1 position). When we meet a pair in the wrong order, we will change them, than we will continue with the next pair. We repeat all of this, if we have changed any pair during last pass.

Using this algorithm, in the first pass, the maximum value has to appear on the correct position. In the next pass, we can skip this value. For n values, in the first pass we have to check n-1 pairs, in the second n-2, in the third n-3, and so on. Usually, we don't need to check, if there was any position changing, if we assume the worst possible situation; the program will be slower, but simple (two nested cycles). This simplest variant of the bubble-sort is very popular and often used for small number of sorted values.

For changing of two elements, we use word "swap". Processor itself has instruction for this, but in the higher level languages, we have to use a working variable. For example, for swapping a and b, we need a c:

c:=a;
a:=b;
b:=c;

The swapping or data manipulation is not a time consuming, because of in advanced programming (later), we will use pointers for everything and we will swap only them. Problem is the data comparison; for a compare, we often need a complex function. An example - for comparing two string in the Czech language, we have to call Windows API. For making the application universal, it is important to ask Windows for this, because environment can be changed (program transfered to Greece, etc.).

Example of bubble-sort with the additional variable for the variable to remember, if any pair has been swapped:

The array declaration:

var a : array[1..10] of integer;

Another variables:

sw : Boolean; {will be true, if swapped}
i : integer; {working variable for this cycle - only one for bubble-sort}
x : integer; {working variable for swapping two elements of the array, the same type as this array}

For testing, generate random array content:

for i:=1 to 10 do a[i]:= random(100);

For our program, create one button for filling with the random values (and their listing to the memo), and the second for sorting (and then listing to the memo).

The program:

Declaration of the array as the global variable:

var
  Form1: TForm1;
  a : array[1..10] of integer;

The first button for filling the array with random values:

procedure TForm1.Button1Click(Sender: TObject);
  var
    i : integer;
begin
  Memo1.Lines.Clear;     {important - at the first, clear it}
  for i:=1 to 10 do begin
      a[i]:= random(100);
      Memo1.Lines.Add(IntToStr(a[i]));
    end;
end;

The sorting button (for the 10 elements, only 9 comparisons!):

procedure TForm1.Button2Click(Sender: TObject);
  var
    sw : Boolean;
    i, x : integer;
begin
  //sorting in the array
  repeat
    sw := False;  {be optimistic}
    for i:=1 to 9 do   //only one command inside outer for-do, no begin-end needed
      if a[i]>a[i+1] then begin
          x:=a[i];
          a[i]:=a[i+1];
          a[i+1]:=x;
          sw:=True;     {we will have to test it again}
        end;
  until not sw;      
  //list them to the memo
  Memo1.Lines.Clear;
  for i:=1 to 10 do Memo1.Lines.Add(IntToStr(a[i]));
end;
Repetition / how to write a program:
From most important command, usually in the center of the algorithm, to outside:

1) the base operation of the sorting is the comparison:
if a[i]>a[i+1] then ...
If this two elements will be in the wrong order, we will change them. If we write:
a[i]:=a[i+1];
, we will lose the a[i], so add the value keeping command before:
x:=a[i];
a[i]:=a[i+1];
a[i+1]:=x;

If we use a new variable, immediately prepare its definition (not persistent, so local variable):
var
i, x : integer;

(the cycle variable has been prepared, because we know, that there will be a cycle).

If there are three commands in the case, that the condition is valid (true), add the begin - end ("program brackets"):
if a[i]>a[i+1] then begin
x:=a[i];
a[i]:=a[i+1];
a[i+1]:=x;

end;
This step will be done for all of the pairs (for 10 elements, we will compare 9 pairs), so write a cycle command before this:
for i:=1 to 9 do ...
There is no begin-end needed, from the cycle point of view, there are only one command (the comparison).
Now the outside cycle - if there were any pair swapped, we have to repeat it again; so we have to try it at least once, so use the repeat - until cycle.
The local variable definition for this variable:
var
sw : Boolean;

For this, add - just after swapping - the value change:
sw:=True;
To make the previous logic, we have to set the opposite value before cycle - variable initialization:
(before the for - do cycle):
sw := False;
Before this, we can write the start of the outside cycle:
repeat
and the until on the end of the sequence - the cycle finishes, if there are no swapping:
until not sw;
The last action is to write the array content to the memo. Use Ctrl+C and Ctrl+V.

International character sets in MS Windows

Simple compare of two strings in Pascal (use <, <=, >, etc.), will compare the strings byte by byte by the order of the character, sorting all uppercase letters before all lowercase. This is not correct. For English, we can use the UpCase function (i.e., for copy of the string, character by character), then compare; universal solution is to use the API function for this:

AnsiCompareText will compare two strings, sorting the uppercase and the lowercase to the same position (in the most cases, this is the correct one).

AnsiCompareStr takes the case (uppercase/lowercase) into comparison, moving the uppercase character to the start. For a special purpose only.

For working with PChar, we have one more function:

function AnsiStrComp(S1, S2: PChar): Integer;
(working with PChar type is too complex to do, leave it to C++ programmers.

All of them are function, but the result is not just True-False, but:

"Condition Return Value"
S1 > S2 , the result will be > 0
S1 < S2 , the result will be < 0
S1 = S2 , the result will be 0 .

Strings can be sorted directly in the Memo1 component, just create a local string variable. Rest of the program is similar, then the previous one:

procedure TForm1.Button4Click(Sender: TObject);
  var
    sw : Boolean;
    i : integer;
    s : string[250];
begin
  //sort directly in the memo component
  repeat
    sw := False;
    for i:=0 to Memo1.Lines.Count - 2 do
      if AnsiCompareText(Memo1.Lines[i],
            Memo1.Lines[i+1]) >0 then begin
          s:=Memo1.Lines[i];
          Memo1.Lines[i]:=Memo1.Lines[i+1];
          Memo1.Lines[i+1]:=s;
          sw:=True;
        end;
  until not sw;
  
end;

We can speed up the program by switching off the redrawing:

SendMessage(self.Handle, WM_SETREDRAW, 0, 0);
{
some long program
}
SendMessage(self.Handle, WM_SETREDRAW, 1, 0);
Form1.Redraw;

To redraw changed object on the screen, we can call the Form1.Update method. To redraw everything, we can call Form1.Refresh method (useful, if anybody modify a part of our form by another window/program).

But still the best method is to create own variable of the TStrings type or array of strings, copy everything here, sort it and copy back. I would only display, how many swaps has been done in the last cycle passing.

The image from my testing program is on the right; change number of elements in the array and appropriate cycle limits to 100 to try this. This program can be downloaded from here.

There are buttons for loading and saving; try to find (on the internet?) a text file to sort.

 

Note - Quick sort: qsort.pas by Borland.