HomePage | About Us | HTML | DelpHi | Downloads | Credits | | BookMark This Site |
|
|
- 2 - More on Pascal
You now have a pretty good start on learning Object Pascal. In this chapter, you will continue to learn about the Object Pascal language by examining more of the fundamentals of Object Pascal. Today you will learn about
if, then, else There are some aspects of programming that are common to all programming languages. One such item that Object Pascal has in common with other programming languages is the if statement. The if statement is used to test for a condition and then execute sections of code based on whether that condition is True or False. Here's an example:
var This code gets the contents of an edit control and stores it in an integer variable. If the number is greater than 10, the expression x > 10 evaluates to True and the message is displayed; otherwise, nothing is displayed. Note that when the conditional expression evaluates to True, the statement immediately following the if...then expression is executed. The conditional part of an if statement is always followed by then. New Term: The if statement is used to test for a condition and execute one or more lines of code when that condition evaluates to True.
Executing Multiple Instructions Let's say you have multiple lines of code that should be executed when the conditional expression is True. In that case, you would need begin and end keywords to block those lines:
if X > 10
then begin When the conditional expression evaluates to False, the code block associated with the if expression is ignored and program execution continues with the first statement following the code block.
Adding else In some cases, you might want to perform an action when the conditional expression evaluates to True and perform some other action when the conditional expression evaluates to False. You accomplish this by implementing the else statement:
if X = 20
then New Term:
The else statement is used in conjunction with the if statement and
identifies a section of code that is executed when the if statement
fails (that is, evaluates to False). In this example, one of the two functions will be called based on the value of X, but not both. I want you to notice something about the preceding example. The line following the if statement does not end in a semicolon. This is because the entire if...then...else sequence is viewed as a single statement. You omit the semicolon on the first line following the if statement only if it's a single line of code (that is, you are not using begin and end following the if statement). Here are a couple of examples of legal if...then...else syntax:
if X = 20
then
NOTE:
Remember that the equality operator is the equal sign (=) and that the
assignment operator is colon-equal (:=). A common coding mistake is to
use the assignment operator where you mean to use the equality operator.
Fortunately, the compiler will issue an error when you do this.
Nested if Statements You can nest if statements when needed. Nesting is nothing more than following an if statement with one or more additional if statements.
if X > 10
then Keep in mind that these are simplified examples. In the real world, you can get lost in the maze of begin and end statements that separate one code block from the next. Take a look at this code snippet, for instance:
if X >
100 then begin end else if
X < -100 then begin Even this is a fairly simple example, but you get the idea.
So far I have used only one conditional expression in the if examples I have given you. When you have just one conditional expression, you can use parentheses around the expression or not use parentheses as you see fit. If, however, you have more than one conditional expression, you must surround each conditional expression with parentheses. For example:
if (X = 20)
and (Y = 50) then If you forget the parentheses, the compiler will, of course, let you know by issuing a compiler error. The if statement is heavily used in Object Pascal programming. It's straightforward, so you won't have any trouble with it. The main thing is keeping all the begin and end keywords straight!
The if...then...else Statement, Form 1 if cond_expr
then If the conditional expression cond_expr is True, the line of code represented by true_statement is executed. If the optional else clause is specified, the line of code represented by false_statement is executed when the conditional expression cond_expr is False.
The if...then...else Statement, Form 2 if
cond_expr_1 then begin If the conditional expression cond_expr_1 is True, the block of code represented by true_statements is executed. If it is False, the block of code represented by false_statements is executed.
Using Loops The loo p is a common element in all programming languages. A loop can be used to iterate through an array, to perform an action a specific number of times, to read a file from disk...the possibilities are endless. In this section, I will discuss the for loop, the while loop, and the repeat loop. For the most part they work in very similar ways. All loops have these common elements:
A loop is an element in a programming language that is used to perform an action repeatedly until a specific condition is met. The starting point for the loop is one of the Object Pascal loop statements (for, while, or repeat). The body contains the statements that will execute each iteration through the loop. The body can contain any valid Object Pascal code and can be a single line of code or multiple lines of code. If the body contains multiple lines of code, the code must be blocked with begin and end statements (with the exception of the repeat loop). The ending point for the loop is either the end keyword (in the case of the for loop and the while loop) or the until keyword (in the case of the repeat loop). When the body of a loop is a single line of code, the begin and end keywords are not required. Most loops work something like this: The loop is entered and the test condition is evaluated. If the test condition evaluates to False, the body of the loop is executed. When program execution reaches the bottom of the loop, it jumps back to the top of the loop where the test condition is again evaluated. If the test condition is still False, the whole process is repeated. If the test condition is True, program execution jumps to the line of code immediately following the loop code block. The exception to th is description is the repeat loop, which tests for the condition at the bottom of the loop rather than at the top. The test condition tells the loop when to stop executing. In effect the test condition says, for example, "Keep doing this until X is equal to 10," or "Keep reading the file until the end-of-file is reached." After the loop starts, it continues to execute the body of the loop until the test condition evaluates to True.
Given that general overview, let's take a look at each type of loop individually.
The for Loop The for loop is probably the most commonly used type of loop. It takes two parameters: the starting value and ending value. If the loop is to count up, the to keyword is used. If the loop is to count backward, then the downto keyword is used.
The for Loop Statement, Counting Up < PRE>for initial_value to end_value do begin statements; end; The for loop repeatedly executes the block of code indicated by statements until the ending value end_value is reached. The state of the loop is initialized by the statement initial_value. The variable indicated in initial_value is incremented by one each iteration through the loop. If the body of the loop is a single statement, the begin and end statements are not required.
The for Loop Statement, Counting Down for
initial_value downto end_value do begin The for loop repeatedly executes the block of code indicated by statements until the ending value end_value is reached. The state of the loop is initialized by the statement initial_value. The variable indicated in initial_value is decremented by one each iteration through the loop. If the body of the loop is a single statement, the begin and end statements are not required. As most syntax statements are somewhat vague, some examples will probably help. First, take a look at a typical for loop that counts up:
var This code will result in the statement inside the braces being executed 10 times. The first parameter, I := 0, tells the for loop that it is starting with an initial value of 0. The second parameter, 9, tells the loop to keep running until the variable I equals 9. The to keyword specifies that the value of I should be incremented by one each time the loop executes.
Let's look at a variation of this code. The following code s nippet will achieve exactly the opposite effect as the first example:
var This time I'm starting with 9 and stopping when I is equal to 0. The downto keyword specifies that the value of I should be decremented each time the loop executes. This is an example of a loop that counts backward.
A Sample for Loop Let's write a little program that illustrates the use of the for loop. In doing so, I will explain another Delphi component, the Memo component (used earlier in this chapter). Perform these steps:
Run the program. When you click the button, lines of text are added to the memo. Figure 2.1 shows this program running. As I said earlier, the loop variable will be incremented by one each time through the loop. Unlike other programming languages, Pascal doesn't provide a way of iterating through a for loop by a value other than one. For example, there is no way to iterate through a for loop from 0 to 100 by 10s. To accomplish this, you must make use of another variable as follows:
var This code will display this in the memo:
Iteration
value: 0 FIGURE 2.1. The output from the for loop exercise.
The Pred and Succ Functions You will often see the Pred and Succ functions used with for loops . The Pred function returns the predecessor of the passed argument. For example, Pred(10) will return the value 9, Pred(100) will return 99, and so on. Given that information, the following three for loops are identical:
var DoSomething; end; When you start with an initial value of 0, it's natural to make the mistake of doing one too many iterations in a loop. Using the Pred function solves this problem and is a bit more elegant than using X - 1. The Succ function, naturally, returns the successor of the argument passed. This is useful when counting backward:
for I := 100
downto Succ(X) do Now that you've seen the for loop in action, it won't be too difficult to apply the same concepts to the while and repeat loops. Let's take a look at those now.
The while Loop The while loop differs from the for loop in that it contains a test condition that is checked at the start of each iteration. As long as the test condition is True, the loop keeps running.
var In this example, I am calling a function that I assume will eventually return a value greater than or equal to 1,000. As long as the return value from this function is less than 1,000, the while loop continues to run. When the variable X contains a value greater than or equal to 1,000, the test condition yields False and program execution jumps to the first line following the body of the while loop. A common implementation of a while loop uses a Boolean test variable. The state of the test variable can be set somewhere within the body of the loop:
var At some point it is expected that the variable Done will be True, and the loop will terminate. Let's do another simple program that illustrates the use of the while loop. Start a new application and place a button and a memo on the form. Double-click the button and modify the event handler so that it looks like this:
procedure
TForm1.Button1Click(Sender: TObject); When you run the program and click the form's button, you will see this text in the memo:
Today I have
5 problems to worry about. This program declares a variable, I, and initializes it to a value of 5. Next, a while loop is started. Text is added to the Memo component each time through the loop, and the variable I is decremented by one. When I is equal to -1, the loop stops and a final line is added to the memo.
The while Loop Statement while
cond_expr do begin end; The while loop repeatedly executes the block of code indicated by statements as long as the conditional expression cond_expr is True. The state of the loop must be initialized prior to the while statement and modification of the state must be explicit in the block of code. When the conditional expression cond_expr evaluates to False, the loop terminates. If the body of the loop is a single statement, the begin and end statements are not required.
The repeat Loop The repeat loop is nearly identical to the while loop. The distinction between the two is important, though. As you found out in the last exercise, the while loop checks the conditional expression at the top of the loop. In the case of the repeat loop, the conditional expression is checked at the bottom of the loop. For example, here's the previous exercise you did except that a repeat loop has been substituted for the while loop:
procedure
TForm1.Button1Click(Sender: TObject); This code will result in the same text displayed in the memo as the previous exercise. Note that it is not necessary to use begin and end because the repeat keyword marks the beginning of the code block, and the until keyword marks the end of the code block. Whether you use a while or a repeat loop depends on what the loop itself does.
The repeat Loop Statement repeat The repeat loop repeatedly executes the block of code indicated by statements as long as the conditional expression cond_expr is False. The state of the loop must be initialized prior to the repeat statement, and modification of the state must be explicit in the block of code. When the conditional expression cond_expr evaluates to True, the loop terminates.
The goto Statement I'll mention goto just so you know it exists. The goto statement enables you to jump program execution to a label that you have previously declared with the label keyword. The label itself is placed in the code followed by a colon. The following code snippet illustrates:
procedure
TForm1.Button1Click(Sender: TObject); It is not necessary to use begin and end here because all lines of code between the goto statement and the label will be executed.
label The goto statement unconditionally transfers the program execution sequence to the label represented by label_name.
Continue and Break Procedures Before we
leave this discussion of loops, you need to know about two procedures
that help control program execution in a loop. The Continue procedure is
used to force program execution to the bottom of the loop, skipping any
statements that come var The Break procedure is used to halt execution of a loop prior to the loop's normal test condition being met. For example, you might be searching an array of integers for a particular number. By breaking execution of your search loop when the number is found, you can obtain the array index where the number was located:
var Continue and Break are only used within for, while, and repeat loops. If you attempt to use these procedures outside of a loop, the compiler will generate a compiler error that says BREAK or CONTINUE outside of loop. There are many situations in which the Continue and Break procedures are useful. As with most of what I've been talking about, it will take some experience programming in Object Pascal before you discover all the possible uses for these two procedures.
The case Statement The case statement can be considered a glorified if statement. It enables you to execute one of several code blocks based on the result of an expression. The expression might be a variable, the result of a function call, or any valid Object Pascal code that evaluates to an expression. Here is an example of a case statement:
case
AmountOverSpeedLimit of There are several parts that make up a case statement. First, you can see that there is the expression, which in this example is the variable AmountOverSpeedLimit (remember, I warned you about long variable names!). Next, the case statement tests the expression for equality. If AmountOverSpeedLimit equals 0 (0 :), the value 0 is assigned to the variable Fine. If AmountOverSpeedLimit is equal to 10, a value of 20 is assigned to Fine, and so on. In each of the first three cases a value is assigned to Fine and code execution jumps out of the case statement, which means that a case matching the expression has been found and the rest of the case statement can be ignored. Notice that cases 20 and 25 have commas following them, but no statements. If the expression AmountOverSpeedLimit evaluates to 20 or 25, those cases fall through and the next code block encountered will be executed. In this situation, values of 20, 25, or 30 will all result in the same code being executed. Finally, you see the else statement. The code block following the else statement will be executed if no matching cases are found. Inclusion of the else statement is optional. You could write a case statement without an else:
case X of As I said earlier, you might want to use a case statement if you find that you have several if statements back to back. The case statement is a bit clearer to others reading your program.
case
SomeStringVariable of String
values are not allowed, nor are floating-point values.
The case Statement case expr of The case statement offers a way to execute different blocks of code depending on various values of an expression expr. The block of code represented by statements_1 is executed when expr is equal to value_1, the block of code represented by statements_2 when expr is equal to value_2, and so on through the block of code represented by statements_n when expr is equal to value_n. When expr is not equal to any of the value_1 through value_n, the block of code at else_statements is executed. The else statement is optional.
Scope The term scope refers to the visibility of variables within different parts of your program. Most variables have local scope, which means that the variable is visible only within the code block in which it is declared. Take a look at the program in Listing 2.1. (This is the first look you've had at a complete unit as generated by Delphi. There is some code here that you haven't seen before, and I'll explain it all in due time, but for the time being you can ignore the parts you aren't familiar with.) New Term: The term scope refers to the visibility of variables within different parts of your program.
LISTING 2.1. SCOPEU.PAS. 01: unit
ScopeU; The first
thing you might notice (if you're still awake by this time) is that the
variable X is declared three times in this program. It is declared on
line 27 in the implementation section, it is declared on line 33 in the
Button1Click method, Take a closer look at Listing 2.1. The declaration for X on line 37 is inside the local procedure Test and is local to that block of code. (I realize I haven't talked about local functions and procedures yet so I'm getting a bit ahead of myself again. Bear with me; I explain local functions later in the section "Local Functions and Procedures.") Effectively, the X that is declared on line 37 does not exist outside the Test procedure. This variable has local scope. Likewise, the declaration for X on line 33 is local to the Button1Click method and does not exist outside the function. Now look at the variable X declared in the implementation section. T his variable is visible anywhere in this unit. Think about that for a minute. Once inside the Button1Click procedure, there are two variables named X (the one declared in the implementation section and the one declared in the Button1Click method), and both are in scope. Which one is being used? The answer: the one in the Button1Click method, because it has the most immediate scope. The variable X that is declared in the implementation section is said to have unit scope. What this means is that this variable X is available anywhere in the unit. As mentioned earlier, a local variable has precedence over a variable with unit scope. But what if you want to access the unit variable X from inside the Button1Click procedure and not the local variable X? You can qualify the variable. Line 50 of Listing 2.1 contains this line:
Memo1.Lines.Add(`Global
X: ` + IntToStr(ScopeU.X)); As you can see, the variable X is qualified with the unit name (ScopeU) followed by the dot operator. Qualifying the variable with the unit name says, "Give me the unit variable X and not the local variable X." (The dot operator is also used with records and classes, but I'll get to that when I talk about classes later.) As I said, when the unit variable X is declared in the implementation section, it has unit scope. If you want a variable to be available to other units in the project, you should declare the variable in the interface section (the variable Form1 in Listing 2.1 is declared in this way). A variable declared in the interface section can be accessed from other units in the project. A variable declared in this way is often referred to as a global variable. To access a variable declared in the interface section of a unit requires nothing more than adding the unit to the uses list and accessing the variable as you would any other variable. If any units in the project have variables with the same name, the variables can be qualified with the unit name as described earlier.< /P>
Records A record is a collection of related data rolled up into a single storage unit. For instance, let's say you want to keep a mailing list. It would be convenient to use a single data variable to hold all the fields needed in a typical mailing list. A record enables you to do that. You first declare the record and then later create an instance of that record when you want to use the record. A record is declared with the record keyword:
MailingListRecord
= record Each of the elements in a record is called a field. Notice that each of the fields must be declared just as if it were a variable in a code block. This record example has five string fields and one integer field. (My apologies to my friends around the world if this looks like a U.S.-slanted mailing-list record.) A zip code/postal code field should really be a string as well, but I want to show you a record with more than one data type. A record is a collection of related data identified as a single storage unit. After a record is declared, an instance of that record can be created for use. Each of the elements in a record is called a field.
Now that the record is declared, it can be put to use. I first need to create an instance of the record. Here's how that looks:
var This statement allocates memory for the record and assigns that memory to a variable named Record. Now that I have an instance of the record set up, I can assign values to the fields:
MLRecord.FirstName
:= `Bruce'; This code snippet contains some syntax you haven't seen yet (although it is very similar to earlier examples when I was discussing qualifying variables). To access the fields of a record, you need to employ the structure member selector operator, commonly called the dot operator. The dot operator is a period placed between the variable name and the field name. If you forget to add the record member operator, you will probably find the compiler complaining about undefined symbols. The record member operator enables you to access a particular member of the record--either to read the value of the field or to change the value of the field. Here's an example of placing the contents of a particular field in a record into an label on a form:
Label1.Caption
:= The record Statement name =
record The record statement declares a grouping of fields (field_1, field_2, ..., field_n) and provides a name for this grouping (name).
The with Statement As long as I am talking about records, let me introduce the with statement. Use of the with statement is not limited to records, but this is a good place to illustrate how the with statement is used. Earlier I gave you this example of filling in a structure: MLRecord.FirstName := `Bruce';
MLRecord.LastName
:= `Reisdorph'; The with statement can be used to help simplify this code. Here is the same code, but implementing the with statement:
with
MLRecord do begin The with statement says, "With this object (MLRecord) do the following...." Notice that when the with statement is implemented, you no longer have to qualify the field names with the record identifier and dot operator. Everything within the begin and end blocks is assumed to belong to the MLRecord object, so qualifying the field names is unnecessary. The with statement can save you a lot of typing and can also make the code more readable.
Arrays of Records Just as you can have arrays of Integers, Chars, or Words, you can also have arrays of records. Declaring and using an array of records is not terribly complicated:
var This is only slightly more complicated than using an array of one of the integral data types. You will notice that the subscript operator and the record member operator are used together to retrieve the value of a field from a specific position in the array.
Include Files Sometimes Pascal programmers use include files. An include file can contain any code that you don't want in your main source unit. Typically, use of include files is reserved for constants or compiler directives that are intended to be used by many other files in the project. An include file is nothing more than a text file with and extension of .INC. The INC extension is not a requirement, but it is customary. Listing 2.2 shows an example of an include file.
LISTING 2.2. TEST.INC. const To create an include file, you simply start with a new text file and save it with an extension of INC. First, choose File | New from the main menu. Next, double-click on the Text icon in the New Items dialog. A new text file will be created and opened in the Code Editor. Enter code and then save the file by choosing File | Save As from the main menu. Be sure to give the file an INC extension or the file will be saved with a TXT extension by default. To use an include file, y ou use the $I compiler directive in any other units that need to use the declarations in the include file. It looks like this:
unit Unit2; StdCtrls;
{$I
Test.inc} The $I compiler directive tells the compiler to compile the contents of the include file into the unit at that point. It's as if the include file were pasted into the unit at that point. You need to be sure that any code in the include file is syntactically correct, or a compiler error will be generated. Don't be too concerned if this is a little confusing right now. It will probably take some experience writing real programs for all this to come together for you.
Functions, Procedures, and Methods Functions and procedures are sections of code separate from the main program. These code sections are executed when needed to perform specific actions in a program. For example, you might have a function that takes two values, performs a complex mathematical calculation on those two values, and returns the result. You might need a function that takes a string, parses it, and returns a portion of the parsed string. You can call (use) these functions any time throughout your programs. Functions and procedures can collectively be called subroutines. (While the term subroutine is not commonly used in Pascal, it is a convenient word to cover both functions and procedures, so I'll use it here.) Subroutines are an important part of any programming language, and Object Pascal is no exception. The simplest type of subroutine takes no parameters and returns no value. Other subroutines might take one or more parameters and might return a value. Rules for naming functions and procedures are the same as those discussed earlier for variables. New Term: A function is a section of code separate from the main progra m that performs some action and returns a value. New Term: A parameter is a value passed to a function or procedure that is used to alter its operation or indicate the extent of its operation. Figure 2.2 shows the anatomy of a function. FIGURE 2.2. The anatomy of a function. New Term: A procedure is a section of code separate from the main program that performs some action but does not return a value. Figure 2.3 shows the anatomy of a procedure. New Term: A method is a function or procedure that is a member of a class. As you can see from these descriptions, the only difference between a function and a procedure is that a function returns a value and a procedure does not return a value. FIGURE 2.3. The anatomy of a procedure. Let's write a program that uses a function. Once again, start with a new application. Then perform the following steps:
FIGURE 2.4. The Code Editor showing the Multiply function.
Run the program and click the button. The label will change to 200 when you click the button. Here's how it works: When you click the button, the Button1Click event handler is called. This, in turn, calls the Multiply function, passing the values of 10 and 20 as parameters. The result is stored in the variable X, which is then displayed in the label.
You might be thinking, "Okay, but how does the product of the two numbers get back from the function?" Take another look at the Multiply function:
function
Multiply(Num1, Num2 : Integer) : Integer; Result := Num1 * Num2;
end; Every Object Pascal function has a local variable called Result. This variable is declared invisibly by the compiler, and it is used to hold the return value from the function. To return a specific value from a function, then, is a simple matter of assigning that value to the Result variable within the function.
The Multiply function can be called in one of several ways. You can pass variables, literal values, or even the results of other function calls. For example:
X :=
Multiply(2, 5); { passing literal values } Notice in the preceding example that the return value is not used. In this case, it doesn't make much sense to call the Multiply function and ignore the return value, but ignoring the return value is something that is done frequently in Object Pascal programming. There are many functions that perform a specific action and then return a value indicating the status of the function call. In some cases the return value is not relevant to your program, so you can just ignore it. If you don't do anything with the return value, it is simply discarded and no harm is done. Now let's add a procedure to the program by following these steps:
Now run the program again. This time when you run the program, the result of the Multiply function is shown in the label as before, and then a message box appears. The message box is shown as a result of calling the SayHello procedure. Calling the SayHello procedure is extremely simple because the procedure takes no parameters. It's important to understand that the code in a function or procedure is executed only if you specifically call the function or procedure from somewhere in your code.
Subroutines can (and frequently do) call other subroutines. Subroutines can even call themselves. This is called recursion and is one way to get into trouble when programming! Recursion is best left alone until you've put in some time with the Object Pascal language. New Term: Recursion is the process by which a procedure or function calls itself.
Declaration and Definition Functions and procedures often have a declaration and always have a definition. New Term: A declaration is a single statement that describes a method's name and parameters, if any. In the case of a function, the declaration also indicates the function's return type. New Term: A function or procedure's definition is the actual body of the function or procedure in the implementation section of the unit. There are three primary cases where a declaration is necessary:
I haven't used declarations up to this point, only definitions. This is because the function definition a lways came before the place in the code where the function was actually used. Take the Multiply function, for example. If I had written a function declaration for this function, it would look like this:
function
Multiply(Num1, Num2 : Integer) : Integer; As you can
see, the function declaration simply describes the function. Function and procedure declarations are placed in the interface section. Placing a declaration in the interface section automatically makes that function or procedure available to other units (makes it public, so to speak). If you don't want the function or procedure to be visible to other units, you can't use a declaration. Instead, you will have to make sure that the function or procedure is defined near the top of the interface section so that it can be seen by all other methods in the unit that use the function. As I said, the examples of functions and procedures up to this point have used this method. I could have done it the other way and used both declaration and definition. Here's part of a unit that contains a declaration for the Multiply function, a Button1Click method that calls the Multiply function, and the definition of the Multiply function:
unit Unit1; In this case the declaration is necessary because the Multiply function is defined after the Button1Click method that calls it. The declaration tells the compiler that a function can be found later in the unit. You'll learn more about function declarations tomorrow when we talk about methods in classes.
Value, Constant, and Reference Parameters Parameters to functions or procedures can be of at least three different types (more than three, actually, but I'll only discuss three types here).
Value Parameters First, parameters can be value parameters. All the parameters you have seen up to this point have been value parameters. The value parameter acts like a local variable in the function or procedure. You can modify the variable within the function and the original variable will remain unchanged. Let's create a new function that illustrates the point. This function will be called SquareAndMultiply. It will take two numbers, square them, multiply them together, and return the result. Here it is:
function
SquareAndMultiply(Num1, Num2 : Integer) : Integer; Now let's look at the code that will call this function:
procedure
TForm1.Button1Click(Sender: TObject); If you want, you can enter this code to test it out. Two values are passed to SquareAndMultiply. The two values are modified inside the SquareAndMultiply function because the numbers need to be squared before they are multiplied together. However, the original values of X and Y in the Button1Click method do not change. When a function uses a value parameter, the compiler first makes a copy of the variable passed to the function and then sends the copy to the function. The original variable is unchanged because the copy is sent to the function and not the actual variable.
Constant Parameters Another way to send values to functions is to use constant parameters. A constant parameter cannot be changed inside the function body. Here's an example of a procedure that takes a constant parameter:
procedure
SaySomething(const S : string); This is one
of the few code examples in this book that contains an error (I hope!).
The compiler will issue an error on the first line in this procedure.
The compiler error will say, Left side cannot be assigned to. The error
is generated because Reference Parameters A third way to send values to functions is to use reference parameters. When you use a reference parameter, the compiler does not make a copy of the object as it does when using value parameters. Rather, the actual variable is passed. This means that any changes made to the variable in the function or procedure will modify the original variable. The following is an example of a procedure that uses a reference parameter (both the procedure and the use of the procedure are shown):
procedure
Square(var Number : Integer); First look at the Square procedure. Notice that the variable parameter is designated by using the var keyword. Because the var keyword is used to declare reference parameters, those parameters are commonly called var parameters. I'll use the terms interchangeably in this section.
Notice also that the body of the function modifies the variable Number by multiplying it times itself. Next, notice the code in the Button1Click method that calls Square. First the variable X is assigned a value. Next, that variable is passed to the Square procedure. After Square executes, the value of X will be 400. Because Square takes a variable parameter, the variable passed to the procedure (X in this case) will be modified. Use variable parameters when you want the procedure or function to make some change to a variable. The fact that an object can be modified by the function or procedure is the most important aspect of variable parameters. Because Square uses a variable parameter you must pass a variable of the same type as the variable parameter. You cannot, for example, do this:
Square(30); This code will generate a compiler error because you can't pass a literal value for a variable parameter. This won't compile either:
var In this case, X is declared as a Word, and the variable parameter of the Square procedure is declared as an Integer. The compiler will generate an error because the types don't match. The compile error generated is Types of actual and formal var parameters must be identical.
Local Functions and Procedures A local function or procedure is a subroutine that is contained within another subroutine. Here's an example:
procedure
TForm1.Button1Click(Sender: Note that the procedure called Test is contained within the var section of the Button1Click procedure. A procedure declared in this way is called a local procedure because it is local to the function or procedure in which it is contained. A local subroutine can be called only from the containing routine; it cannot be called from anywhere else in the program. An important fact of local procedures and functions is that the variables of the containing procedure are available inside the local subroutine. In this example, the variable X is available in the main body of the Button1Click procedure and in the local procedure. When this code executes, the memo component will contain this text:
Main
Function, X = 100 This illustrates that the variable X is available in the local procedure as well as in the main procedure.
Method Overloading Starting with Delphi 4, Object Pascal enables you to work with functions that have the same name but take different parameters. New Term: Method overloading is having two or more procedures or functions with the same name but with different parameter lists. Methods that share a common name are called overloaded methods. Earlier I showed you a sample program that contained a function called Multiply. Not surprisingly, this function multiplied two values together. The function took two integers, multiplied them, and returned the result. What if you want to have the function multiply two Doubles or two Words? Previous versions of Delphi would require you to have several functions:
{
declarations for a program written in Delphi 1, 2, or 3 } Wouldn't it be a lot easier if you could just have one function called Multiply that would be smart enough to know whether you wanted to multiply Integers, Doubles, or Words? That is now possible in Delphi thanks to function overloading. Here's what the declarations for an overloaded function look like:
{
declarations in Delphi 4 } You still have to write separate functions for each of these declarations, but at least you can use the same function name. The compiler takes care of calling the correct function based on the parameters you pass to the function. For example:
var The compiler sees that two Doubles are passed to the function and calls the version of the Multiply function that takes two Doubles for parameters. Likewise, if two Integers are passed, the compiler calls the version of Multiply that takes two Integers.
Default Parameters for Functions A procedure or function can have default parameters that, as the name implies, supply a default value for a parameter if no value is specified when the procedure or function is called. A function implementing a default parameter might look like this:
{ Procedure
declaration. } You can call this function with or without a parameter. If the parameter is supplied at the time the function is called, the function behaves as a regular function would. If the parameter is not supplied when the function is called, the default parameter is used automatically. Given this example, the following two lines of code are identical:
Redraw; As you can see, when a parameter has a default value, it can be omitted from the function call altogether. When declaring functions and procedures, you can mix default and nondefault parameters in the same function:
{
declaration } Loop : Boolean = False; Loops : Integer = 10) : Integer;
{ calls to
PlayWaveFile } Default parameters are helpful for many reasons. For one thing, they make your life easier. You might have a function that you call with the same parameters 99 percent of the time. By giving it default parameters, you shorten the amount of typing required each time you make a call to the function. Whenever you want to supply parameters other than the defaults, all you have to do is plug in values for the default parameters.
Summary This chapter contains essential information on some of Object Pascal's basic operations. You need to understand what is presented here in order to program in Delphi. First, you learned about the different types of loops in Object Pascal, and then you learned about the case statement and how to use it. I talked a little about scope and what that means to your variables. Then you found out about records and how they can be used in your programs. You finished the day by learning about functions and procedures.
Workshop The Workshop contains quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you have learned. You can find answers to the quiz questions in Appendix A, "Answers to the Quiz Questions."
Q&A
Quiz
Exercises
|
s