In the
previous tutorial, you've learned about constants, variables, their definitions and assignment. We haven't talked about any environment from which these variables can be accessed and used. Now it's time to do some work and perform some actions. The article you're going to read below covers basic usage of procedures and functions in Delphi. So let's get started and do some programming!
In Delphi, there are a few ways to do some real work (calculations etc.): in a function, in a procedure, in a project source. We will be talking about the first two for now. A function or procedure is a block of code, that can be called (executed) from another source. Basically, they are meant to do a single action or realise a single algorithm. For example, there could be a function to generate ten random numbers: it might consist of lots of code, but in the end it does a single job. At first let's find out the difference between a function and a procedure. Procedures are meant to be only called to do something, but the function can also return some result. For example, if you want to print 10 lines of random numbers, you could use a procedure to do this, but if you want to get a random number and store it in memory, you could use a function to generate it and return it back to the code it was called from. Function results can be assigned to variables or simply ignored. To use a function or a procedure, we must first define it. So let's define two global functions and i'll walk you through every line, explaining what it does. As we already know, global stuff is defined either in classe's public clause or simply in a unit outside all classes. If you define a procedure for a class - it is no longer a procedure. It is called a method in terms of object orientated programming. Though there's not much of a difference between them two. Look at the following lines of code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
public
end;
procedure DisplayMsg(AMessage : string);
function Add(AVar1, AVar2 : real): real;
var
Form1: TForm1;
implementation
end.
As you see, we have a function and a procedure defined just before the var directive in this example. Now let's find out what both of these lines mean.
procedure DisplayMsg(AMessage : string); - This line defines a procedure, that will have a parameter of string type, passed to it. What does that mean? It means that when you call this procedure, you'll have to specify some text. To make things even clearer - this procedure will show us a message with an OK button on screen. So, if we want to show some message, we need to know what text to output, right? To know what text to show, we add a parameter
AMessage that is basically same as a variable. Now, when users want to call this procedure, they already know wht text they'll want to show and specify this text in the place where
AMessage is. To Call a procedure or a function, we simply write its name, followed by an opening bracket, followed by all its parameter values and a closing bracket.
Notice that each line of actual code (not directives) in Delphi must be terminated by a semicolon. So if we want to show a message "Hello to the world!" on the screen, we would call the
DisplayMsg procedure like this:
DisplayMsg('Hello to the world!');
You can see that in the procedure call, variable (parameter)
AMessage is replaced by its value. You can also replace it with some other variable that you have defined earlier.
function Add(AVar1, AVar2 : real): real; - Now this is a little bit different. We see a function to add a number to another number and return the result. We see thet this function takes two parameters:
AVar1 and
AVar2. These parameters will later be added to each other in the function and returned. The last
real word after the closing bracket defines a type that this function returns.
So the definition of a function or a procedure looks like this:
procedure ProcedureName(Parameter1: type1; Parameter2: type2; ...; ParameterN: typeN);
function FunctionName(Parameter1: type1; ...; ParameterN: typeN): ReturnType;
As you can see, we use semicolons to separate each parameter and in previously declared function
Add we've used commas. There aren't any mistakes here. The rules apply the same way as in variable definitions: you can list parameters of a same type, separated by commas, but you must separate different type parameters by semicolons.
OK so we've defined a function and a procedure. Now what? Let's make them work!. As the definition part is done in the
interface section of the unit, the real code is done in
implementation. So see the example below and try to understand it.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
public
end;
procedure DisplayMsg(AMessage : string);
function Add(AVar1, AVar2 : real): real;
var
Form1: TForm1;
implementation
procedure DisplayMsg(AMessage: string);
begin
end;
function Add(AVar1, AVar2 : real): real;
begin
end;
end.
Here we have started realising our methods. The single line function and procedure definitions are only meant to show us how the function calls will look like (how they are called, what parameters they have, what they return) and the lines with begin ... end clauses area meant to show us what the methods actually do. In this case they do absolutely nothing, because we've coded nothing yet. So as you have probably guessed, this is default implementations of our procedure and a function. First line is the same as in the definition, then it is followed by the word
begin, then some empty space 9that we will insert some code later) and finally, by the word
end; (note the semicolon). Same goes to both procedures and functions.
Begin and
end; blocks do exactly what you might have already guessed - they mark the start and the end of some code block (in this case, procedure or a function). Let's advance this example a bit more. Let's replace the implementations with the ones below:
procedure DisplayMsg(AMessage: string);
begin
ShowMessage('This is our message: ' + AMessage);
end;
function Add(AVar1, AVar2 : real): real;
begin
result := AVar1 + AVar2;
end;
Now we have some code in them. What does it do? Let's start with the procedure. We see a single line of code that basically calls another procedure, called
ShowMessage and passes our defined message with a prefix "This is our message: ". Note that you can "glue" two strings together by using the + operator. For example if you write
some_string_var := 'Two ' + 'words'; - the value of
some_string_var will become "Two words". We've done the same in the brackets of
ShowMessage in the current example. Whatever the user passes to
AMessage, will be prefixed by "This is our message: " and then sent to the
ShowMessage procedure. But hold on a moment ... where did this
ShowMessage come from? We haven't seen it before. Correct, we haven't seen lots of things yet, but some of them already exist in Delphi.
ShowMessage is a procedure, created by Borland team and integrated to Delphi so that you wouldn't need to create something like this for yourself. If you deleted the
Dialogs word from the
uses clause in the top of your unit - this procedure would not work, because
Dialogs is exactly the unit this procedure is implemented in. In order to really learn Delphi, you will have to know many procedures like this one, or at least know where to find information about them. The reason is simple - there is no need to invent anything that has already been invented. Imagine how much time you would spend, writing all the already implemented procedures yourself. It's just a waste of time. But let's get back to the point. If we call the
DisplayMsg procedure with a parameter "Hello world", it will be executed like this:
A text "This is our message: " would be prefixed to the start of "Hello world" and the result of those two "glued" strings would be sent to
ShowMessage procedure. And the
ShowMessage would display a message with OK button that says: "This is our message: Hello world".
The function is a little different. We see that the sum of our variables (parameters)
AVar1 and
AVar2 is being assigned to a variable
result. But we also haven't defined this variable anywhere! You guessed it - it has already been defined for us. And in this case it isn't even a variable - it's the result of our function. So what all this line of code means is that both the parameters are summed and returned by the function to the place where it was called from. Now to illustrate this returning mechanism, see the following example.
function ModifyWord(AWord : string) : string;
begin
result := AWord + ' (modified)';
end;
procedure DisplayWord(AWord: string);
begin
ShowMessage(ModifyWord(AWord));
end;
Here we have a function and a procedure again. I've not defined them to leave only the most important code and to keep it short. But i hope you understand that the rest of the unit, including definitions have to be here too in order for the code to work :) So imagine we call
DisplayWord procedure with parameter "Hello". What happens here?
1. The procedure
DisplayWord is passed a parameter "Hello".
2. Since Delphi reads the lines in the exect opposite as we do, it firsts tries to call
ModifyWord function with a parameter that we have already passed.
3. The function
ModifyWord is called and passed a parameter "Hello".
4. ModifyWord adds the text " (modified)" to the end of the parameter, it results in "Hello (modified)".
5. ModifyWord assigns "Hello (modified)" to the result of itself and returns the result to the caller.
6. The caller is
DisplayWord procedure and with the result from
ModifyWord returned, it sees the
ShowMessage... line like this:
ShowMessage("Hello (modified)").
7. The
ShowMessage is called with a parameter, returned by the
ModifyWord function.
8. The "Hello (modified)" message is displayed on the screen with an OK button.
It might have been simpler to understand if the procedure looked like this:
procedure DisplayWord(AWord: string);
var _temp : string;
begin
_temp := ModifyWord(AWord);
ShowMessage(_temp);
end;
First thing to note is that we have a variable
_temp, defined in the procedure. This is called a local variable. Only this procedure can see and work with it. It is defined between the
Procedure line and the
begin word by the same rules, as the variables are defined in a unit. Note that you don't have to prefix local variables with "_", but it will be easier for you to differ local variables from global if you do so. So in the above example, we create a new string variable
_temp to store a temporary value. Then, we assign whatever the function
ModifyWord("Hello") returns. And then we display the contents of
_temp variable in a message box. Another thing to note is that you don't have to define a local variable in this situation. You can do this:
procedure DisplayWord(AWord: string);
begin
AWord := ModifyWord(AWord);
ShowMessage(AWord);
end;
Parameters are also variables so they can also be assigned values. Since Delphi sees the lines from the opposite way that we do, it first takes the value of
AWord ("Hello") and passes it to
ModifyWord function. Then it takes the result, returned by
ModifyWord and assigns to
AWord. So we've just changed the value of the same variable we already had. And finally we passed its value to
ShowMessage.
You can safely assign new values to function or pracedure parameters as long as you know for sure you won't need the old ones :) Now try to see if you've understood anything and try to figure out what message we would see on the screen if we called
DisplayMsg('cat') in the following code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
public
end;
function ModifyText(AText : string): string;
procedure DisplayMsg(AMsg : string);
var
Form1: TForm1;
glob1 : string = 'my ';
implementation
function ModifyText(AText : string): string;
begin
AText := AText + ' eats a lot';
result := AText;
end;
procedure DisplayMsg(AMsg : string);
begin
AMsg := ModifyText(AMsg);
AMsg := glob1 + AMsg;
ShowMessage(AMsg);
end;
end.
If you thought of 'my cat eats a lot' - you were right! Congratulations! If not, let's see what happened here.
1. You've passed a text "cat" to the
DisplayMsg procedure.
2. It then called
ModifyText with the same parameter "cat".
3. ModifyText appended text " eats a lot" to the word "cat" and assigned it to the result.
4. ModifyText returned its result back to
DisplayMsg procedure's first line and that result was assigned back to
AMsg. So the
AMsg now contains text "cat eats a lot" instead of "cat".
5. Another string from a global variable
glob1 was prefixed to the
AMsg and assigned back to it. So now
AMsg contains text "my cat eats a lot".
6. Finally the
ShowMessage was called with a parameter "my cat eats a lot".
Someday you will ask yourself a very important question: can a function return more that one value? What if i need it to return two or three numbers, instead of one? The answer is: by default no, but there are still other ways to do it. By using those ways we can even make a procedure return values! Now i'm going to explain only one of the few possible ways, because you still only have enough knowledge to understand the first way. The answer to our prayers is the
var directive. If written before a parameter name, it enables this parameter to be read from outside the function. There are two types of parameters: passed by value and passed by reference. The ones we've covered so far were passed by value. That is in all cases, if we wanted to call a procedure with a parameter, we would have to replace the parameter's name with some value. And in the function or procedure we were able to change this parameter safely, without affecting the outside world. But if we pass a parameter by reference - then some variable is passed instead of its value and we can no longer replace parameter names with values. We have to replace them with other variable names instead. Consider the following example:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
public
end;
procedure ModifyText(var AText : string);
procedure DisplayMsg(AMsg : string);
var
Form1: TForm1;
glob1 : string = 'my ';
implementation
procedure ModifyText(var AText : string);
begin
AText := AText + ' eats a lot';
end;
procedure DisplayMsg(AMsg : string);
var _temp : string;
begin
_temp := AMsg;
ModifyText(_temp);
AMsg := glob1 + _temp;
ShowMessage(AMsg);
end;
end.
As you can see here, we have a
var directive before the
AText parameter in procedure
ModifyText. As you can also see, we got rid of the function and replaced it with a procedure instead. Let's see what happens here.
1. We pass the text "cat" again to the
DisplayMsg.
2. To make it less confusing, the procedure assigns the value of
AMsg to a temporary variable
_temp.
3. Now we call another procedure
ModifyText with parameter
_temp.
4. Normally, a word "cat" would be passed to
ModifyText, but when we have a
var directive, all the variable
_temp is passed instead of its value. Actually, its address is passed instead of its value, but let's not confuse you with the technical side yet.
5. Now in the
ModifyText procedure, the
AText variable, instead of having a value of "cat", points to
DisplayMsg procedure's variable
_temp. So whatever we do with the value with
AText, the same will reflect on
_temp in
DisplayMsg. In this case we append some text to the
AText parameter. So this means that instead of appending it to
AText, delphi appended to
_temp. In other words,
AText becomes something like an alias of the
_temp variable and instead of having its own value,
AText sees the value of
_temp. We don't have to assign anything to
result here, because we've assigned directly to
_temp and procedures don't have a
result directive.
6. Now, after appending " eats a log" to
_temp, which already had "cat" in it, the result of
_temp is "cat eats a lot"
7. Now the result of
_temp is "glued" to the result of
glob1 and assigned back to AMsg. So now
AMsg contains text "my cat eats a lot".
8. Finally, we display the message.
Hopefully, you've learned some valuable information from this tutorial. In the next tutorial, we will talk about something very similar to functions and procedures - methods.
Click here to continue reading