logo

 

     
 
Home
Site Map
Search
 
:: Bitwise Courses ::
 
Bitwise Dusty Archives
 
 
 

rss

 
 

ruby in steel

learn aikido in north devon

Learn Aikido in North Devon

 


Section :: Pascal & Delphi

- Format For Printing...

Learn To Program Delphi Part Two

Procedures, functions, constants, sets and more…
Sunday 1 October 2006.
 

In the first lesson in this series, we wrote a French verb conjugator. This looked like a very simple program. Unfortunately, simple programs can rapidly turn into quite complicated ones and this is a case in point.


Zip - 38.4 kb
Right-click to download the source code

See also: Part One of this Series

Download Turbo Delphi from: http://www.turboexplorer.com/

Initially, when it was dealing with just one verb type, it seemed reasonable to place all the code into Delphi’s Button1Click event-handler.

But now I want it to handle all the regular verb forms, ‘-er’, ‘-re’ and ‘-ir’. Were I to put the code into a single procedure, my program would soon become messy and difficult to debug. So I’ve decided to clean it up by giving each verb-form its own procedure.

On Call

Load up the french3.dpr project. You will see that I’ve written three procedures, each of which begins with its name followed by a string parameter between brackets, similar to the following:

procedure ConjugateIRverb( vStem : string );

Unlike the procedures which Delphi generates automatically (for example, when you double-click a button on a form), my procedures are not preceded by the TForm1. qualifier. This is because my procedures and functions use standard Pascal syntax whereas Delphi’s self-generated procedures use an extended object-orientated syntax. We’ll be looking at Delphi’s object orientation next month.

Calling a procedure is perfectly straightforward. When a procedure has no parameters, you just specify the procedure name. If you look at the Syntax Summary box at the end of this article, you’ll see that the ChangeCaption procedure is used in this way. When a procedure has one or more parameters, the type of each parameter must be declared in the procedure heading. The code which calls the procedure must supply the correct number and type of parameters between brackets. The NewCaption procedure demonstrates this syntax.

There is no requirement to use the same name for the parameter in the calling code and in the procedure heading, just so long as the parameter types match. Look at the ConjugateIRverb procedure in the french3.dpr project. I have named the procedure’s string parameter vStem. But in the line of code which calls this procedure, I have used a variable called verbStem:

ConjugateIRverb( verbStem )

The important thing is not the parameter’s name but its type. Both vStem and verbStem are strings so they are completely compatible. If verbStem had been an integer, the program would not even compile.

GIF - 7.4 kb
The first version of my verb conjugator deals reasonably well with regular French verbs but it doesn’t get rid of the ‘e’ and add an apostrophe to ‘Je’ when it comes before a vowel. We’ll fix that shortly…

Once More, With Feeling

Even though my changes have added some extra features and tidied up the code, I still need to do more to turn this into a useful application. Load up my next version, french4.dpr. You’ll find that I’ve now added more procedures to deal with some irregular verbs. More interestingly from a programming point of view, you’ll see that I have also shortened some of the statements.

Due to Delphi’s scoping rules and Object Pascal’s dot-notation, the earlier version of the program had to use numerous lines of code such as:

Form1.ListBox1.Items.Add('Tu ' + vStem + 'is' );

Each procedure contained a list of eight of these code lines in which only the part between parentheses differed. Fortunately, Pascal has a way of cutting repetitious code down to size. The with reserved word lets you specify the duplicated part of the code just once, followed by the do keyword. For example, instead of writing Form1.ListBox1.Items.Add on each and every line, I can write this just once:

with Form1.ListBox1.Items do

I can now follow this with a block of code between the begin and end keywords. Each line of code in this block ‘knows’ that it is dealing with Form1.ListBox1.Items, and only needs to tag on any item (such as the Add method, minus the preceding dot) which would normally be tagged onto the end of the code between with and do

The rewritten code is much more terse:

with Form1.ListBox1.Items do
begin
 Add('Tu ' + vStem + 'is' );
 Add('Il ' + vStem + 'it' );
 { and so on… }
end;

If this is the first time you’ve come across Pascal’s with, you may find it useful to try out my withtest.dpr demo project, which shows a quick way of setting several properties of an Edit field.

Constant Companions

There was another niggling glitch in the early version of my verb conjugator. In French, Je is supposed to contract to J’ when it precedes a verb beginning with a vowel. Rather than handle this separately in each procedure, in french4.dpr I’ve written a Pascal function called JePlusStem which takes a string parameter (the verb stem), appends this to the correct form of Je and returns the result. This is the complete function:

function JePlusStem( vStem : string ) : string;
Const
 vowels : set of char = ['a','e','i','o','u','y'];
begin    
  if vStem[1] in vowels then
     JePlusStem := 'J''' + vStem
  else
     JePlusStem := 'Je ' + vStem;
end;

Note that, a Pascal function differs from a procedure in these ways:

1) A function returns a value. A procedure does not.
2) The function name must be preceded by the word function.
3) A colon and return type must appear after the function header.

There are two ways of indicating the value to be returned by a function. The traditional Pascal syntax, requires that you assign the value to the function name (e.g. JePlusStem := 'Je ' + vStem). Delphi has an alternative syntax which lets you use an implicit variable called result (e.g. result := 'Je ' + vStem). In this series, I shall be using only the traditional syntax.

Another feature worth noting in the function shown above is the Constant declaration:

const
 vowels : set of char = ['a','e','i','o','u','y'];

Constants in Pascal are, generally, unchangeable values. They are often used as references which can be checked elsewhere in the program. For example, the Constants shown below might be used to store the resolution of the screen at design time, thereby allowing forms and buttons to make comparisons with the current screen resolution and rescale themselves as necessary:

const
  ScreenHeight    = 600;
  ScreenWidth     = 800;

Another common use for Constants is to store a string indicating the application name or version number:

const
    appName = ‘French Verbs’;
    version = ‘2.0b’;

More examples of simple Constants can be found in the constant.dpr project. But the Constant declaration in my JePlusStem function is a bit more complicated since it doesn’t define a single value. It defines a ‘set’ of values. You can think of a set as a kind of collection of values with the same ordinal (or ‘sequential’) type. Your program can test a value for membership of a set using the reserved word in. For example, in my program, this test would evaluate to True:

if ‘e’ in vowels

Search the Delphi Help index for more information and try out the example in our sets.dpr project.

By now it should be clear that Pascal programs are more easily maintainable when they are divided up into procedures and functions. Moreover, the careful use of variables, constants and sets in conjunction with operators such as with and in can help make your code understandable.

Even so, there remain many important programming issues we have not yet considered. We still haven’t found how to use loops to cycle repeatedly through actions. And we have barely even touched upon Delphi’s object orientated extensions to the Pascal language. These are just some of the subjects we’ll be tackling next month.


Variables and Parameters

Most of the variables used in our sample programs are declared ‘locally’ inside procedures or functions. This makes them inaccessible to other procedures.

But there are many occasions when multiple procedures need access to a particular variable. One way of accomplishing this is to declare a variable ‘globally’ at the top of the unit. Load up the globvar.dpr project. You will see that the string variable, ‘s’, has been declared globally. When the following procedure is executed, it has the effect of transforming ‘s’ into lower case:

procedure proc1;
begin
 s := LowerCase( s );
end;

There are problems with global variables, however. If I had another procedure, for example, which used a local variable called ‘s’, any assignments to ‘s’ would affect only the local variable and not (as I might suppose) the global one. Global variables can lead to obscure bugs and should be avoided whenever possible.

When you want to share a variable with another procedure, it is better to pass it as a parameter. The declaration of the parameter in the procedure header determiners whether or not the original variable will be altered when the procedure executes.

If a parameter is declared simply, it will be passed ‘by value’. This means that the parameter is a copy of the original variable. Any alterations made to the copy will not affect the original. Here ‘s’ is a value parameter:

procedure proc1( s : string );

If a parameter is preceded by the word var, however, it is said to be passed ‘by reference’. A parameter of this sort can be regarded as, in effect, the same as the original variable (that is, it is not a separate copy) and any alterations made to it will affect the original. Here ‘s’ is a var parameter:

procedure proc2( var s : string );

The sample project, funcproc.dpr should help clarify the differences between these two parameter types.


SYNTAX SUMMARY
This code fragment summarises the syntax needed to declare and call standard Pascal procedures and functions. You can try out this code in the syntax2.dpr project.
procedure ChangeCaption;
begin
 Form1.Caption := 'Wheee!';
end;

procedure NewCaption( nc, nc2 : string );
begin
 Form1.Caption := nc + nc2;
end;

function TheFormCaption : string;
begin
 TheFormCaption := Form1.Caption;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  NewCaption( 'Hello', ' world' );
  ChangeCaption;
  Button3.Caption := TheFormCaption;
end;

See also: Part One of this Series

AddThis Social Bookmark Button


Home