This documentation and the accompanying software, including all of the example Pascal programs and text files, are protected under United States copyright law to protect them from unauthorised commercialisation. This version of the tutorial is distributed under the shareware concept, which means you are not required to pay for it. You are permitted to copy the disks, and pass copies on to a friend, provided you do not modify any files or omit any files from the complete package. In fact, you are encouraged to pass complete copies on to friends. You are permitted to charge a small fee to cover the costs of duplication, but you are not permitted to charge anything for the software itself. If you find the tutorial helpful, you are encouraged to register with the author and to submit a small fee to help compensate him for his time and expense in writing it. We will provide you with a beautifully printed copy of this tutorial if you submit a full registration. See the READ.ME file in the TEXT directory for additional details. Whether or not you send a registration fee, feel free to request a copy of the latest list of available tutorials and a list of the authorised Public Domain libraries that distribute our full line of programming language tutorials. Gordon Dodrill - Feb 4, 1991
Copyright (c) 1986, 1988, 1989, 1991, Coronado Enterprises Coronado Enterprises 12501 Coronado Ave NE Albuquerque, New Mexico 87122
ABOUT THE AUTHOR The author of this tutorial began programming in 1961 using FORTRAN on an IBM 1620. Since then, most of his career has been involved with designing digital logic for satellite application. In 1983, being somewhat burned out with logic design, he began a study of some of the more modern programming languages and has since made a complete career shift to software development. After learning Pascal, C was studied, followed by Modula-2 and Ada, and more recently C++. Rather than simply learning the syntax of each new language, modern methods of software engineering were studied and applied to effectively utilise the languages. He is currently employed by a large research and development laboratory where he continues to study, teach, and apply the newer programming languages.
CORONADO ENTERPRISES ..............................................................................................................1 TURBO PASCAL TUTOR - VERSION 2.6.........................................................................................1 INTRODUCTION...................................................................................................................................6 IF YOU KNOW NOTHING ABOUT PASCAL..................................................................................6 LARGER PASCAL PROGRAMS .......................................................................................................6 WHAT IS A COMPILER? ...................................................................................................................6 WHICH VERSION OF TURBO PASCAL? ........................................................................................7 EARLY VERSIONS OF TURBO PASCAL ........................................................................................7 WHAT ABOUT TURBO PASCAL VERSION 5.5 & 6.0? .................................................................7 PREPARATION FOR USE OF THIS TUTORIAL .............................................................................8 WHAT ABOUT THE PROGRAMMING EXERCISES? ....................................................................8 THE ANSWERS DIRECTORY ...........................................................................................................8 A SPECIAL NOTE FOR THE SHAREWARE VERSION..................................................................8 CHAPTER 1 : WHAT IS A COMPUTER PROGRAM? ...................................................................9 THIS CHAPTER IS FOR NEW PROGRAMMERS............................................................................9 WHAT IS A COMPUTER PROGRAM? .............................................................................................9 WHAT ARE CONSTANTS? ...............................................................................................................9 WHAT ARE VARIABLES? ................................................................................................................9 HOW DO WE DEFINE CONSTANTS OR VARIABLES? ................................................................9 WHAT IS SO GOOD ABOUT PASCAL?.........................................................................................10 YOUR FIRST PASCAL PROGRAM ................................................................................................11 WHAT IS AN IDENTIFIER?.............................................................................................................11 NOW FOR THE PROGRAM.............................................................................................................12 A PROGRAM THAT DOES SOMETHING......................................................................................12 ANOTHER PROGRAM WITH MORE OUTPUT ............................................................................13 ADDING COMMENTS IN THE PROGRAM...................................................................................13 THE RESULT OF EXECUTION SECTION .....................................................................................14 GOOD FORMATTING PRACTICE..................................................................................................14 VERY POOR FORMATTING PRACTICE.......................................................................................15 PROGRAMMING EXERCISES ........................................................................................................16 WHAT IS A DATA TYPE? ...............................................................................................................17 OUR FIRST VARIABLES.................................................................................................................17 OUR FIRST ARITHMETIC...............................................................................................................18 NOW LET'S USE LOTS OF VARIABLES .......................................................................................19 BOOLEAN VARIABLES ..................................................................................................................21 WHERE DO WE USE THE BOOLEAN VARIABLES? ..................................................................22 SHORT CIRCUIT OR COMPLETE EVALUATION? .....................................................................23 LET'S LOOK AT THE CHAR TYPE VARIABLE ...........................................................................23 EXTENDED INTEGER TYPES ........................................................................................................24 EXTENDED REAL TYPES...............................................................................................................25 PROGRAMMING EXERCISE ..........................................................................................................26 THE FOR LOOP.................................................................................................................................27 A COMPOUND PASCAL STATEMENT .........................................................................................28 THE IF STATEMENT .......................................................................................................................28 THE IF-THEN-ELSE BLOCK ...........................................................................................................29 LOOPS AND IFS TOGETHER .........................................................................................................30 FINALLY, A MEANINGFUL PROGRAM.......................................................................................31 THE REPEAT UNTIL LOOP ............................................................................................................32 THE WHILE LOOP ...........................................................................................................................33 THE CASE STATEMENT.................................................................................................................34 PROGRAMMING EXERCISES ........................................................................................................36
CHAPTER 5 : PROCEDURES AND FUNCTIONS .........................................................................37 A PASCAL PROGRAM OUTLINE ..................................................................................................37 THE FIRST PROCEDURES ..............................................................................................................37 DEFINITIONS GO IN THE DEFINITION PART ............................................................................38 HOW TO DEFINE & CALL A PROCEDURE..................................................................................39 THE UNBROKEN RULE OF PASCAL............................................................................................39 MORE PROCEDURE CALLS...........................................................................................................39 FORMAL AND ACTUAL PARAMETERS......................................................................................40 CALL BY VALUE .............................................................................................................................41 CALL BY REFERENCE....................................................................................................................41 SOME NEW TERMINOLOGY .........................................................................................................41 "CALL BY REFERENCE" OR "CALL BY VALUE"?.....................................................................42 A MULTIPLY DEFINED VARIABLE .............................................................................................43 PROCEDURES CALLING OTHER PROCEDURES .......................................................................44 NOW LET'S LOOK AT A FUNCTION ............................................................................................45 NOW FOR THE MYSTERY OF RECURSION ................................................................................45 ABOUT RECURSIVE PROCEDURES.............................................................................................46 THE FORWARD REFERENCE ........................................................................................................46 THE PROCEDURE TYPE .................................................................................................................48 PROGRAMMING EXERCISES ........................................................................................................50 CHAPTER 6 : ARRAYS, TYPES, CONSTANTS, AND LABELS..................................................51 ARRAYS ............................................................................................................................................51 USING THE ARRAY.........................................................................................................................52 DOUBLY INDEXED ARRAYS ........................................................................................................52 ARRAYS ARE FLEXIBLE................................................................................................................54 THE TYPE DEFINITION ..................................................................................................................54 PASCAL CHECKS TYPES VERY CAREFULLY ...........................................................................54 IS THE CONCEPT OF "TYPES" IMPORTANT?.............................................................................55 THE CONSTANT DECLARATION .................................................................................................55 THE TURBO PASCAL TYPED CONSTANT ..................................................................................56 THE LABEL DECLARATION..........................................................................................................56 THE PACKED ARRAY.....................................................................................................................57 ONE MORE TURBO PASCAL EXTENSION..................................................................................58 PROGRAMMING EXERCISES ........................................................................................................58 CHAPTER 7 : STRINGS & STRING PROCEDURES ....................................................................59 PASCAL STRINGS............................................................................................................................59 A STRING IS AN ARRAY OF CHAR ..............................................................................................59 THE TURBO PASCAL STRING TYPE............................................................................................60 STRINGS HAVE VARIABLE LENGTHS........................................................................................60 WHAT'S IN A STRING TYPE VARIABLE? ...................................................................................60 PROGRAMMING EXERCISE ..........................................................................................................61 CHAPTER 8 : SCALARS, SUBRANGES, AND SETS.....................................................................62 PASCAL SCALARS ..........................................................................................................................62 A BIG SCALAR VARIABLE LOOP.................................................................................................63 LET'S LOOK AT SOME SUBRANGES ...........................................................................................63 SOME STATEMENTS WITH ERRORS IN THEM. ........................................................................64 THREE VERY USEFUL FUNCTIONS ............................................................................................65 SETS ...................................................................................................................................................65 SEARCHING WITH SETS ................................................................................................................67 PROGRAMMING EXERCISE ..........................................................................................................68 CHAPTER 9 : RECORDS ...................................................................................................................69 A VERY SIMPLE RECORD .............................................................................................................69 A SUPER RECORD ...........................................................................................................................70 HOW TO MANIPULATE ALL OF THAT DATA............................................................................71 WHAT IS THE WITH STATEMENT? .............................................................................................72 HOW FAR DOWN CAN YOU NEST THE WITH STATEMENT? ................................................72
SUPER-ASSIGNMENT STATEMENTS ..........................................................................................72 WHAT GOOD IS ALL OF THIS .......................................................................................................72 A VARIANT RECORD......................................................................................................................73 WHAT IS A TAG-FIELD?.................................................................................................................74 USING THE VARIANT RECORD....................................................................................................74 NOW TO SEE WHAT WE HAVE IN THE RECORDS ...................................................................75 PROGRAMMING EXERCISE ..........................................................................................................75 CHAPTER 10 : STANDARD INPUT/OUTPUT................................................................................76 WE'VE USED THIS ALREADY .......................................................................................................76 MANY OUTPUT STATEMENTS.....................................................................................................77 NOW FOR SOME INPUT FROM THE KEYBOARD .....................................................................77 TIME TO CRASH THE COMPUTER...............................................................................................79 READING REAL NUMBERS ...........................................................................................................79 READING CHARACTER DATA .....................................................................................................79 BULLET PROOF PROGRAMMING ................................................................................................80 HOW DO I PRINT SOMETHING ON THE PRINTER? ..................................................................80 PROGRAMMING EXERCISE ..........................................................................................................81 CHAPTER 11 : FILE INPUT/OUTPUT ............................................................................................83 FILES HANDLE SERIAL DATA......................................................................................................83 A SHORT HISTORY LESSON .........................................................................................................83 BACK TO THE PRESENT TIME .....................................................................................................83 READING AND DISPLAYING A FILE...........................................................................................83 WHAT ARE THE "EOF" AND "EOLN" FUNCTIONS?..................................................................84 A PROGRAM TO READ ANY FILE................................................................................................85 HOW TO COPY A FILE (SORT OF) ................................................................................................86 USING A COMPILER DIRECTIVE .................................................................................................87 WE DO OUR OWN FILE CHECKING.............................................................................................87 HOW TO READ INTEGER DATA FROM A FILE .........................................................................87 READ AND READLN ARE SLIGHTLY DIFFERENT ...................................................................89 NOW TO READ SOME REAL VARIABLES FROM A FILE.........................................................89 NOW FOR BINARY INPUT AND OUTPUT ...................................................................................90 WHY USE A BINARY FILE .............................................................................................................92 READING A BINARY FILE .............................................................................................................92 FILE POINTERS, GET, AND PUT STATEMENTS ........................................................................93 PROGRAMMING EXERCISES ........................................................................................................93 CHAPTER 12 : POINTERS AND DYNAMIC ALLOCATION ......................................................94 THIS IS ADVANCED MATERIAL ..................................................................................................94 WHAT ARE POINTERS, AND WHAT GOOD ARE THEY? .........................................................94 HOW DO WE USE THE POINTERS? ..............................................................................................95 A FEW MORE POINTERS................................................................................................................95 THIS IS FOR TURBO PASCAL ONLY............................................................................................95 OUR FIRST LOOK AT DYNAMIC ALLOCATION .......................................................................96 WHAT IS THE HEAP? ......................................................................................................................97 WHAT IS DYNAMIC ALLOCATION?............................................................................................97 GETTING RID OF DYNAMICALLY ALLOCATED DATA..........................................................98 DYNAMICALLY STORING RECORDS .........................................................................................98 WE JUST BROKE THE GREAT RULE OF PASCAL ...................................................................100 THIS IS A TRICK, BE CAREFUL ..................................................................................................100 WHAT GOOD IS THIS ANYWAY?...............................................................................................101 WHAT IS A LINKED LIST? ...........................................................................................................101 STILL NO VARIABLES?................................................................................................................103 WHAT IS "NIL" AND WHAT IS IT USED FOR?...........................................................................103 DEFINING THE SECOND RECORD .............................................................................................103 TEN MORE RECORDS...................................................................................................................103 FINALLY, A COMPLETE LINKED LIST .....................................................................................104 HOW DO WE GET TO THE DATA NOW? ...................................................................................104 PROGRAMMING EXERCISE ........................................................................................................104
CHAPTER 13 : UNITS IN TURBO PASCAL .................................................................................105 PART OF A PROGRAM..................................................................................................................105 THE INTERFACE PART.................................................................................................................106 THE IMPLEMENTATION PART ...................................................................................................107 A LOCAL PROCEDURE.................................................................................................................107 WHAT IS THE BODY USED FOR? ...............................................................................................107 SELECTIVE NAMING OF FUNCTIONS AND PROCEDURES ..................................................107 ANOTHER UNIT.............................................................................................................................108 HOW DO WE USE OUR DEFINED UNITS?.................................................................................109 ONE MORE EXAMPLE OF UNIT USE.........................................................................................109 MULTIPLE USES OF AN IDENTIFIER ........................................................................................110 WHY USE UNITS?..........................................................................................................................111 THIS IS INFORMATION HIDING .................................................................................................111 PROGRAMMING EXERCISE ........................................................................................................111 CHAPTER 14 : ENCAPSULATION & INHERITANCE ..............................................................112 OUR FIRST ENCAPSULATION ....................................................................................................112 WHAT IS A METHOD? ..................................................................................................................113 THE METHOD IMPLEMENTATION ............................................................................................114 AN INSTANCE OF AN OBJECT....................................................................................................114 NEW TERMINOLOGY ...................................................................................................................114 WHAT DID WE ACCOMPLISH?...................................................................................................115 DATA & CODE PROTECTION......................................................................................................115 MORE ENCAPSULATION .............................................................................................................115 THE PRIVATE TYPE......................................................................................................................117 A FEW RULES ARE NEEDED.......................................................................................................118 WHAT IS A CONSTRUCTOR? ......................................................................................................118 WHAT IS A DESTRUCTOR? .........................................................................................................119 OUR FIRST INHERITANCE ..........................................................................................................119 HOW DO WE USE THE OBJECTS? ..............................................................................................121 WHY USE INHERITANCE? ...........................................................................................................122 AN OBJECT IN A UNIT .................................................................................................................122 ANOTHER OBJECT IN A UNIT ....................................................................................................123 USING THE OBJECTS DEFINED IN UNITS................................................................................125 AN ARRAY AND A POINTER ......................................................................................................126 WHAT IS MULTIPLE INHERITANCE?........................................................................................127 WHAT SHOULD YOU DO NOW?.................................................................................................127 PROGRAMMING EXERCISES ......................................................................................................127 CHAPTER 15 : VIRTUAL METHODS ...........................................................................................128 WITHOUT A VIRTUAL METHOD................................................................................................128 NOW TO MAKE IT A VIRTUAL METHOD.................................................................................130 ASSIGNING DESCENDANTS TO ANCESTORS? .......................................................................132 WHY USE A CONSTRUCTOR?.....................................................................................................132 VIRTUALS AND POINTERS .........................................................................................................133 AN ANCESTOR OBJECT ...............................................................................................................135 SOME DESCENDENT OBJECTS ..................................................................................................136 A COMPLETE EMPLOYEE PROGRAM.......................................................................................137 PROGRAMMING EXERCISES ......................................................................................................138 AMORTIZATION TABLE GENERATOR .....................................................................................139 TOP DOWN PROGRAMMING ......................................................................................................139 MOST IMPORTANT - YOUR OWN PROGRAMS................................................................................141
INTRODUCTION
IF YOU KNOW NOTHING ABOUT PASCAL
Assuming you know nothing at all about Pascal, and in fact, that you may know nothing about programming in general, we will begin to study Pascal. If you are already somewhat familiar with programming and especially Pascal, you will probably want to skip very quickly through the first few chapters. You should at least skim these chapters, and you should read the remainder of this introduction. A few comments are in order to get us started in the right direction. The sample programs included on the disks are designed to teach you the basics of Pascal and they do not include any clever or tricky code. Nearly all of the programs are really quite dumb as far as being useful programs, but all will teach one or more principles of Pascal. I have seen one tutorial that included a 12 page program as the first example. In fact there were only 2 example programs in the entire tutorial, and it was impossible to glean the essentials of programming from that system. For this reason, I will completely bypass any long programs until the very end of this tutorial. In order to illustrate fundamental concepts used in Pascal programming, all programs will be very short and concise until we reach the last chapter.
WHAT IS A COMPILER?
There are two methods used to run any computer program that is written in a readable form of English. The first method is to use an interpreter. An interpreter is a program that looks at each line of the "English" program, decides what the "English" on that line means, and does what it says to do. If one of the lines is executed repeatedly, it must be scanned and analysed each time, greatly slowing down the solution of the problem at hand. A compiler, on the other hand, is a program that looks at each statement one time and converts it into a code that the computer understands directly. When the
compiled program is actually run, the computer does not have to figure out what each statement means, it is already in a form that the computer can run directly, resulting in a much faster execution of the program. This tutorial is written especially for Borland International's TURBO Pascal compilers version 5.0 through 6.0. These are very high quality compilers that can do nearly anything you will ask them to do since they are so flexible. The original intent of this tutorial was to write it in such a way that it would be completely generic and usable with any good Pascal compiler. The programmers at Borland included a great many non-standard aids for the Pascal language and resulted in a very good product that has dominated the market for microcomputers. To completely omit all of the extensions would do those of you with the Borland compiler a real disservice, and to include the extensions would not allow other compilers to be used effectively with this tutorial. The decision was made to use the Borland extensions and make the tutorial very difficult to use with other compilers. If you have a need to use Pascal with some other compiler, TURBO Pascal is so inexpensive that it would be a wise decision to purchase a copy solely for the purpose of learning the Pascal programming language, then moving to a larger compiler on a minicomputer or a mainframe using the accumulated knowledge to very quickly learn the extensions provided by that particular compiler. At any rate, this tutorial will not teach you everything you will ever need to know about Pascal. It will, however, teach you the fundamentals and the advanced features of Pascal, but of even more importance is the definition of Pascal terminology needed to progress on your own into more advanced topics of Pascal and programming in general. You will find that experience will be your best teacher.
constants or variables can be first named, then assigned a value. The means for doing this in Pascal will be given throughout the remainder of this tutorial.
WHAT IS AN IDENTIFIER?
All identifiers, including the program name, procedure and function names, type definitions, and constant and variable names, will start with an alphabetical character and be composed of any combination of alphabetic and numeric characters with no embedded blanks. Upper or lower case alphabetic characters are not significant and may be mixed at will. (If you find this definition confusing at this point, don't worry about it, it will be clear later but it must be defined early). The standard definition of Pascal requires that any implementation (i.e. any compiler written by some company) must use at least 8 characters of the identifier as significant and may ignore the remaining characters if more than 8 are used. Most implementations use far more than 8. All versions of TURBO Pascal use 63 characters in an identifier as being significant. Standard Pascal does not allow the use of underlines in an identifier but most implementations of Pascal allow its use after the first character. All versions of TURBO Pascal compilers permit the use of the underline in an identifier, so it will be freely used throughout this tutorial. The underline is used in the program name Puppy_Dog which should be on your display at this time. Returning to the example program, line 2 is a blank line which is ignored by all Pascal compilers. More will be said about the blank line at the end of this chapter.
The special word Writeln is not a reserved word but is defined by the system to do a very special job for you, namely to output a line of data to the monitor. It is, in fact, a procedure supplied for you by the writers of TURBO Pascal as a programming aid for you. You can, if you so desire, use this name for some other purpose in your program, but doing so will not allow you to use the standard output
procedure. It will then be up to you to somehow get your data out of the program. Note carefully that some words are reserved and cannot be redefined and used for some other purpose, and some are special since they can be redefined. You will probably not want to redefine any of the special words for a long time. Until you gain considerable programming experience, simply use them as tools. Notice the semicolon at the end of line 4. This is the statement separator referred to earlier and tells the Pascal compiler that this line is complete as it stands, nothing more is coming that could be considered part of this statement. The next statement, in line 5, is another statement that will be executed sequentially following the statement in line 4. This program will output the two lines of text and stop. Now it is time to go try it. Compile and run the program in the same manner as you did for the first example program. You should see the two lines of text output to the video display every time you run this program. When you grow bored of running WRITESM.PAS let's go on to another example.
program Lots_Of_Comments; begin { This is the start of the main program } (* This is a comment that is ignored by the Pascal compiler *) { This is also ignored } Writeln('I am in Pascal school, Dad'); Writeln('All students are always broke'); (* Writeln('Send money'); Writeln('Send money'); *) Writeln('I am really getting hungry'); end. (* This is the end of the main program *) (* Comment *) {Comment}
{ Result of execution I am in Pascal school, Dad All students are always broke I am really getting hungry }
A fine point should be mentioned here. Even though some compilers allow comments to start with (* and end with }, or to start with { and end with *), it is very poor programming practice and should be discouraged. The ANSI Pascal standard allows such usage but TURBO Pascal does not allow this funny use of comment delimiters. TURBO Pascal does not allow you to nest comments using the same delimiters but it does allow you to nest one type within the other. This could be used as a debugging aid. If you generally use the (* and *) for comments, you could use the { and } in TURBO Pascal to comment out an entire section of code during debugging even if it had a few comments in it. This is a trick you should remember when you reach the point of writing programs of significant size. When you have successfully modified and run the program with comments, we will go on to explain good formatting practice and how Pascal actually searches through your source file (Pascal program) for its executable statements. It should be mentioned that the program named PASCOMS.PAS does not indicate good commenting style. The program is meant to illustrate where and how comments can be used and looks very choppy and unorganised. Further examples will illustrate good use of comments to you as you progress through this tutorial.
is called for as a delimiter. Pascal only uses the combination of reserved words and end-of- statement semicolons to determine the logical structure of the program. Since we have really only covered two executable statements, I have used them to build a nice looking program that can be easily understood at a glance. Compile and run this program to see that it really does what you think it should do.
program Good_Programming_Style; begin Write('Programming style '); Write ('is a matter of '); Writeln ('personal choice'); Write('Each person '); Write ('can choose '); Writeln ('his own style'); Write('He can be '); Write ('very clear, or '); Writeln ('extremely messy'); end.
{ Result of execution Programming style is a matter of personal choice Each person can choose his own style He can be very clear, or extremely messy }
{ Result of execution Programming style is a matter of personal choice Each person can choose his own style He can be very clear, or extremely messy }
UGLYFORM.PAS should be a good indication to you that Pascal doesn't care about programming style or form. Pascal only cares about the structure, including reserved words and delimiters such as blanks and semicolons. Carriage returns are completely ignored as are extra blanks. You can put extra blanks nearly anywhere except within reserved words or variable names. You should pay some
attention to programming style but don't get too worried about it yet. It would be good for you to simply use the style illustrated throughout this tutorial until you gain experience with Pascal. As time goes by you will develop a style of statement indentation, adding blank lines for clarity, and a method of adding clear comments to Pascal source code. Programs are available to read your source code, and put it in a "pretty" format, but that is not important now. Not only is the form of the program important, the names used for variables can be very helpful or hindering as we will see in the next chapter. Feel free to move things around and modify the format of any of the programs we have covered so far and when you are ready, we will start on variables in the next chapter. Be sure you compile and execute UGLYFORM.PAS.
PROGRAMMING EXERCISES
1. 2. Write a program that displays your name on the video monitor. Modify your program to display your name and address on one line, then modify it by changing the Write's to Writeln's so that the name and address are on different lines.
Please note that four of these types of data (char, shortint, word, and longint) are not a part of the standard Pascal definition but are included as extensions to the TURBO Pascal compiler. In addition to the above data types TURBO Pascal version 5.0 and later have the following data types available; single double extended comp Real type with 7 significant digits Real type with 15 significant digits Real type with 19 significant digits The integers from about -10E18 to 10E18
TURBO Pascal version 5.0 and newer have these four types available which use the 80X87 math coprocessor. Because TURBO Pascal has a software emulator for the floating point operations, an 80X87 math coprocessor is not absolutely required to use these new types with these versions. Of course, your resulting program will run much faster if you have the coprocessor available for use by the program. A complete definition of the available types for each compiler can be found in the TURBO Pascal reference manual. It would be good to read these pages now for a good definition prior to learning how to define and use them in a program. Note that all of these will be used in example programs in this chapter.
Notice that there is only one use of the reserved word var, but it is used to define three different variables, Count, X, and Y. Once the word var is recognised, the compiler will continue to recognise variable definitions line after line until it finds another reserved word. It would be permissible to put a var on the second line also but it is not necessary. It would also be permissible to put all three variables on one line but your particular programming style will dictate where you put the three variables. Following the colon on each line is the word integer which is a standard identifier, and is therefore different from a reserved word. A standard identifier is predefined like a reserved word, but you can redefine it, thereby losing its original purpose and meaning. For now and for a long time, don't do that.
(* Chapter 3 - Program 1 *) program Integer_Variable_Demo; var Count X,Y : integer; : integer;
into pieces and the individual pieces are output with Write and Writeln statements. Observe especially that a Writeln all by itself simply moves the cursor to the beginning of a new line on the video monitor. Compile and run this program and observe its output after you are certain that the two programs are actually identical.
(* Chapter 3 - Program 2 *) program More_Integer_Demos; var X,Y Count : integer; : integer;
begin X := 12; Y := 13; Count := X + Y; Write('The value of X is'); Writeln(X:4); Write('The value of Y is'); Writeln(Y:5); Write('And Count is now '); Write(Count:6); Writeln; end.
(* Chapter 3 - Program 3 *) program All_Simple_Variable_Types; var A,B : integer; C,D : byte; Dog_Tail : real; Puppy : boolean; Animal_Cookies : char;
begin A := 4; B := 5; C := 212; D := C + 3; Dog_Tail := 345.12456; Puppy := B > A; (* since B is greater than A, Puppy will be assigned the value TRUE *) Animal_Cookies := 'R'; (* this is a single character *) Writeln('The integers are',A:5,B:5); Writeln('The bytes are', C:5,D:5); (* notice that the spaces prior to the C will be ignored on output *) Writeln('The real value is',Dog_Tail:12:2,Dog_Tail:12:4); Writeln; Writeln('The boolean value is ',Puppy,Puppy:13); Writeln('The char variable is an ',Animal_Cookies); end.
{ Result of execution The integers are The bytes are 212 The real value is 4 5 215 345.12
345.1246 TRUE
{simply assigning a value} {adding a constant} {summing two variables} {multiplication} {division} {subtraction} {example of a multiple expression}
{It will be left up to you to print out some of the above values} end.
The example program named INTMATH.PAS illustrates some of the math capabilities of Pascal when using integer class variables. A byte type variable is used just like an integer variable but with a much
smaller allowable range. Only one byte of computer memory is used for each variable defined as a byte type variable, but 2 are used for each integer type variable.
(* Chapter 3 - Program 5 *) program Integer_Math_Demo; var A,B,C,D : integer; E : real; begin A := B := C := D := E :=
Simple assignment *) simple addition *) simple addition *) multiplication *) integer division with the result expressed as a real number *) D := B div A; (* integer division with the result expressed as a truncated integer number *) D := B mod A; (* d is the remainder of the division, in this case d = 4 *) D := (A + B) div (B + 7); (* composite math statement *)
9; A + 4; A + B; 4*A*B; A/B;
(* (* (* (* (*
BOOLEAN VARIABLES
Let's take a look at a boolean variable, which is only allowed to take on two different values, TRUE or FALSE. This variable is used for loop controls, end of file indicators or any other TRUE or FALSE conditions in the program. Variables can be compared to determine a boolean value. A complete list of the relational operators available with Pascal is given in the following list. = <> > < >= <= equal to not equal to greater than less than greater than or equal to less than or equal to
These operators can be used to compare any of the simple types of data including integer, char, byte, and real type variables or constants, and they can be used to compare boolean variables. An illustration is the best way to learn about the boolean variable so load BOOLMATH.PAS and observe it. In BOOLMATH.PAS we define a few boolean variables and two integer type variables for use in the program and begin by assigning values to the two integer variables. The expression Junk = Who in line 14 is actually a boolean operation that is not true since the value of Junk is not equal to the value of Who, The result is therefore FALSE and that value is assigned to the boolean variable A. The boolean variable B is assigned the value of TRUE because the boolean expression Junk = (Who - 1) is true. The boolean variables C and D are likewise assigned some values in a manner that should not need any comment. After assigning a value to the variable with the big name, the values are all printed out. Note that if either A or B is TRUE, the result is TRUE in line 18.
(* Chapter 3 - Program 6 *) program Illustrate_What_Boolean_Math_Looks_Like; (* notice the program name, it can be up to 63 characters long. Variables can also be very long as we will see below *) var A,B,C,D : boolean; A_Very_Big_Boolean_Name_Can_Be_Used : boolean; Junk,Who : integer; begin Junk := 4; Who := 5; A := Junk = Who; {since Junk is not equal to Who, A is false} B := Junk = (Who - 1); {This is true} C := Junk < Who; {This is true} D := Junk > 10; {This is false} A_Very_Big_Boolean_Name_Can_Be_Used := A or B; {Since B is true, the result is true} Writeln('result A is ',A); Writeln('result B is ',B); Writeln('result C is ',C); Writeln('result D is ',D:12); {This answer will be right justified in a 12 character field} Writeln('result A_Very_Big_Boolean_Name_Can_Be_Used is ', A_Very_Big_Boolean_Name_Can_Be_Used); (* Following are a few boolean expressions. *) B and C and D; B and C and not D; B or C or D; (B and C) or not (C and D); (Junk = Who - 1) or (Junk = Who);
A A A A A end.
:= := := := :=
{ Result of execution result result result result result } A is FALSE B is TRUE C is TRUE D is FALSE A_Very_Big_Boolean_Name_Can_Be_Used is TRUE
Examine the sample program CONVERT.PAS for several examples of converting data from one simple variable to another. The comments make the program self explanatory.
(* Chapter 3 - Program 8 *) program Convert_From_Type_To_Type; var Index,Count Error_Ind Size,Cost Letter Name,Amount : : : : : integer; integer; real; char; string[12];
begin Index := 65; Count := 66; Cost := 124.678; Amount := '12.4612'; Letter := Chr(Index); Size := Count; Index := Round(Cost); Count := Trunc(Cost); (* convert integer to char *) (* convert integer to real *) (* real to integer, rounded *) (* real to integer, truncated *)
Index := Ord(Letter); (* convert char to integer *) Str(Count,Name); (* integer to string of char *) Val(Amount,Size,Error_Ind); (* string to real note that "Error_Ind" is used for returning an error code *) Writeln('Name is ',Name,' and Size is ',Size:10:4); end.
begin Index := MaxInt; Small_int := 127; Pos_int := Index + 256 * Small_int; Big_int := 1000 * MaxInt + 1234; Writeln('Index Writeln('Small_int Writeln('Pos_int Writeln('Big_int Writeln; = = = = ',Index:12); ',Small_int:12); ',Pos_int:12); ',Big_int:12);
{ Result of execution Index Small_Int Pos_Int Big_Int Big_Int } = = = = = 32767 127 65279 32768234 234
It must be pointed out that the calculation in lines 13 and 21 result in a different answer even though they appear to be calculating the same thing. An explanation is in order. The quantity named MaxInt used in lines 10 and 13 is a constant built into the system that represents the largest value that an integer type variable can store. On the first page of this chapter we defined that as 32767 and when running the program you will find that Index displays that value as it should. The constant MaxInt has a type that is of a universal_integer type as do all of the numeric constants in line 13. The result then is calculated to the number of significant digits dictated by the left hand side of the assignment statement which is of type longint resulting in a very large number. When we get to line 21, however, the variable Index is of type integer so the calculations are done as though the constants were of type integer also which causes some of the more significant digits to be truncated. The truncated result is converted to type longint and assigned to the variable Big_int and the truncated value is displayed by line 22. After that discussion it should be apparent to you that it is important what types you use for your variables. It must be emphasised that it would not be wise to use all large type variables because they use more storage space and slow down calculations. Experience will dictate the proper data types to use for each application.
(* Chapter 3 - Program 10 *) program Extended_Real_Types; (* Note: If you are using TURBO Pascal Version 5.0 or newer (* and you do not have a Math Co_Processor, you can (* still compile and run this program by using the (* compiler directive as explained in the User's Guide. var Number Small_Number Big_Number Huge_Number Whole_Number begin Number Small_Number Big_Number Huge_Number Whole_Number : : : : : real; single; double; extended; comp; *) *) *) *)
:= := := := :=
100000000000000000000000000.0; 100000000000000000000000000.0; 100000000000000000000000000.0; 100000000000000000000000000.0; 1000000000000000000.0; = = = = = ',Number :40:3); ',Small_Number:40:3); ',Big_Number :40:3); ',Huge_Number :40:3); ',Whole_Number:40:3);
{ Result of execution Number Small_Number Big_Number Huge_Number Whole_Number } = = = = = 99999999999985900100000000.000 100000002537764290000000000.000 100000000000000005000000000.000 100000000000000000000000000.000 1000000000000000000.000
PROGRAMMING EXERCISE
1. Write a program containing several variable definitions and do some math on them, printing out the results.
begin Start := 1; Ending := 7; for Count := Start to Ending do (* Example 1 *) Writeln('This is a count loop and we are in pass',Count:4); Writeln; Total := 0; for Count := 1 to 10 do begin (* Example 2 *) Total := Total + 12; Write('Count =',Count:3,' Total =',Total:5); Writeln; end; Writeln; Write('The alphabet is '); for Alphabet := 'A' to 'Z' do Write(Alphabet); Writeln;
(* Example 3 *)
{ Result of execution This This This This This This is is is is is is a a a a a a count count count count count count loop loop loop loop loop loop and and and and and and we we we we we we are are are are are are in in in in in in pass pass pass pass pass pass 1 2 3 4 5 6
This is a count loop and we are in pass Count Count Count Count Count Count Count Count Count Count = 1 = 2 = 3 = 4 = 5 = 6 = 7 = 8 = 9 = 10 Total Total Total Total Total Total Total Total Total Total = = = = = = = = = = 12 24 36 48 60 72 84 96 108 120
The alphabet is ABCDEFGHIJKLMNOPQRSTUVWXYZ Decrementing Decrementing Decrementing Decrementing Decrementing Decrementing } loop loop loop loop loop loop 7 6 5 4 3 2
THE IF STATEMENT
Pascal has two conditional branching capabilities, the if and the case statements. We will look at the simplest of the two now, the if statement. Load IFDEMO.PAS for an onscreen look at the if then pair of reserved words first illustrated in lines 11 and 12. Any condition that can be reduced to a boolean answer is put between the if then pair of words. If the resulting expression resolves to TRUE, then the single Pascal statement following the reserved word then is executed, and if it resolves to FALSE, then the single statement is skipped over. Of course, you can probably guess that the single statement can be replaced with a compound statement bracketed with a begin end pair and you are correct. Study example 1 and you will see that the line will always be printed in this particular fragment because Three is equal to One + Two. It is very difficult to come up with a good example without combining some of the other control structures but we will do so in the next example program. The second example in lines 14 through 19, is similar to the first but has the single statement replaced with a compound statement and should be simple for you to understand.
begin (* main program *) One := 1; (* these are to have some numbers *) Two := 2; (* to use for illustrations *) Three := 3; if Three = (One + Two) then (* Example 1 *) Writeln('three is equal to one plus two'); if Three = 3 then begin Write('three is '); Write('equal to '); Write('one plus two'); Writeln; end; (* Example 2 *)
if Two = 2 then (* Example 3 *) Writeln('two is equal to 2 as expected') else Writeln('two is not equal to 2... rather strange'); if Two = 2 then (* Example 4 *) if One = 1 then Writeln('one is equal to one') else Writeln('one is not equal to one') else if Three = 3 then Writeln('three is equal to three') else Writeln('three is not equal to three'); end. (* main program *)
{ Result of execution three is equal to one plus two three is equal to one plus two two is equal to 2 as expected one is equal to one }
The third example in lines 21 through 24, contains a new reserved word, else. When the if condition is FALSE, the single statement following the word then is skipped and if a semicolon is encountered, the if clause is totally complete. If instead of a semicolon, the reserved word else is encountered, then the single Pascal statement following else is executed. One and only one of the two statements will be executed every time this if statement is encountered in the program. Examination of the third example should clear this up in your mind. Notice that the Pascal compiler is looking for either a semicolon to end the if, or the reserved word else to continue the logic. It is therefore not legal to use a semicolon immediately preceding the reserved word else. You will get a compiler error if you include the semicolon. This is a common error, but easy to fix, so you will get used to writing it correctly.
statement is legal without begin end separators. This is shown in the fourth example of the IFDEMO.PAS Pascal example program. Lines 27 through 30 comprise a single Pascal statement, and lines 32 through 35 comprise another. The if statement begun in line 26 therefore has a single statement in each of its branches, and it is a single Pascal statement itself beginning in line 26 and ending in line 35. Reread this paragraph until you understand it completely, because it is a very important concept. The if then else construct is one of the most used, most useful, and therefore most important aspects of Pascal. For this reason you should become very familiar with it. Try changing some of the conditions in the example program to see if you can get it to print when you expect it to for your own practice. When you are ready, we will go on to a program with loops and conditional statements combined and working together.
{ Result of execution The The The The The The The The The The } loop counter is up to loop counter is up to loop counter is up to loop counter is up to loop counter is up to internal loop index is internal loop index is internal loop index is internal loop index is internal loop index is 1 2 3 4 5 8 9 10 11 12
is is is is is
8 8 8 8 8
You should make careful note of the formatting used here. The begin is at the end of the line which starts the control and the end is lined up under the control word such that it is very clear which control word it is associated with. All statements within each control structure are indented three spaces which greatly adds to the readability. You will develop your own clear method of formatting your code in time but until then it is suggested that you follow this example which is written in a manner which is acceptable within the general Pascal programming community.
An easily made error should be pointed out at this time. If an extraneous semicolon were put at the end of the if statement in line 8, the code following the statement would always be executed because the null statement (the nothing statement between the then and the semicolon) would be the conditional statement. The compiler would not generate an error and you would get no warning. Add a semicolon at the end of line 8 to see the error. Of course, you will need to compile and execute the program to see line 9 print for all 10 values of Count.
{ Result of execution Centigrade to farenheight temperature table C C C C C C C C C C C C C C C } = = = = = = = = = = = = = = = -20 -10 0 10 20 30 40 50 60 70 80 90 100 110 120 F F F F F F F F F F F F F F F = = = = = = = = = = = = = = = -4 14 32 50 68 86 104 122 140 158 176 194 212 230 248
Load, examine, and run DUMBCONV.PAS for a good example of poor variable naming. The structure of the program is identical to the last program and when you run it, you will see that it is identical in output, but compared to the last program, it is difficult to understand what it does by studying the listing. We studied UGLYFORM.PAS in chapter 2 of this tutorial and it illustrated really stupid formatting that nobody would ever use. The poor choice of variable names and lack of comments in the present program is nearly as unreadable, but many programmers are content to write code similar to this example. You should be conscious of good formatting style and naming conventions from the start and your programs will be clear, easy to understand, and will run efficiently. This program, like the last should be easily understood by you, so we will go on to our next Pascal control structure.
(* Chapter 4 - Program 5 *) program Temperature_Conversion; var A1,A2,A3 : integer; begin Writeln('Centigrade to farenheight temperature table'); Writeln; for A1 := -2 to 12 do begin A2 := 10*A1; A3 := 32 + A2*9 div 5; Write(' C =',A2:5); Write(' F =',A3:5); if A2 = 0 then Write(' Freezing point of water'); if A2 = 100 then Write(' Boiling point of water'); Writeln; end; end.
{ Result of execution Centigrade to farenheight temperature table C C C C C C C C C C C C C C C } = = = = = = = = = = = = = = = -20 -10 0 10 20 30 40 50 60 70 80 90 100 110 120 F F F F F F F F F F F F F F F = = = = = = = = = = = = = = = -4 14 32 50 68 86 104 122 140 158 176 194 212 230 248
statements between the two reserved words until the boolean expression following the until is found to be TRUE. This is the only expression in Pascal that operates on a range of statements rather than a single statement and begin end delimiters are not required. A word of caution is in order here. Since the loop is executed until some condition becomes TRUE, it is possible that the condition will never be TRUE and the loop will never terminate. It is up to you, the programmer, to insure that the loop will eventually terminate. Compile and execute REPEATLP.PAS to observe the output.
(* Chapter 4 - Program 6 *) program Repeat_Loop_Example; var Count : integer; begin Count := 4; repeat Write('This is in the '); Write('repeat loop, and '); Write('the index is',Count:4); Writeln; Count := Count + 2; until Count = 20; Writeln(' We have completed the loop '); end.
{ Result of execution This is in the repeat loop, This is in the repeat loop, This is in the repeat loop, This is in the repeat loop, This is in the repeat loop, This is in the repeat loop, This is in the repeat loop, This is in the repeat loop, We have completed the loop } and and and and and and and and the the the the the the the the index index index index index index index index is is is is is is is is 4 6 8 10 12 14 16 18
(* Chapter 4 - Program 7 *) program While_Loop_Example; var Counter : integer; begin Counter := 4; while Counter < 20 do begin Write('In the while loop, waiting '); Write('for the counter to reach 20. It is',Counter:4); Writeln; Counter := Counter + 2; end; end.
{ Result of execution In In In In In In In In } the the the the the the the the while while while while while while while while loop, loop, loop, loop, loop, loop, loop, loop, waiting waiting waiting waiting waiting waiting waiting waiting for for for for for for for for the the the the the the the the counter counter counter counter counter counter counter counter to to to to to to to to reach reach reach reach reach reach reach reach 20. 20. 20. 20. 20. 20. 20. 20. It It It It It It It It is is is is is is is is 4 6 8 10 12 14 16 18
The example file uses Count for the case selector, prints the numbers one through five in text form, and declares that numbers outside this range are not in the allowable list. The program should be self explanatory beyond that point. Be sure to compile and run this example program. Load and display the sample program BIGCASE.PAS for another example of a case statement with a few more added features. This program uses the identical structure as the previous program but in line 11 a range is used as the selector so that if the value of Count is 7, 8, or 9 this selection will be made. In line 12, three different listed values will cause selection of this part of the code. Of greater importance are the compound statements used in some of the selections. If the variable Count has the value of 2, 4, or 6, a compound statement will be executed and if the value is 3, a for loop is executed. If the value is 1, an if statement is executed which will cause a compound statement to be executed. In this case the if statement will always be executed because TRUE will always be true, but any Boolean expression could be used in the expression. Be sure to compile and run this program, then study the output until you understand the result thoroughly.
(* Chapter 4 - Program 8 *) program Demonstrate_Case; var Count : integer; begin (* main program *) for Count := 0 to 8 do begin Write(Count:5); case Count of 1 : Write(' One'); (* Note that these do not have *) 2 : Write(' Two'); (* to be in consecutive order *) 3 : Write(' Three'); 4 : Write(' Four'); 5 : Write(' Five'); else Write(' This number is not in the allowable list'); end; (* of case structure *) Writeln; end; (* of Count loop *) end. (* of main program *)
{ Result of execution 0 1 2 3 4 5 6 7 8 } This number One Two Three Four Five This number This number This number is not in the allowable list
is not in the allowable list is not in the allowable list is not in the allowable list
(* Chapter 4 - Program 9 *) program A_Bigger_Case_Example; var Count : integer; Index : byte; begin (* main program *) for Count := 1 to 10 do begin Write(Count:5); case Count of 7..9 : Write(' Big Number'); 2,4,6 : begin Write(' Small'); Write(' even'); Write(' number.'); end; 3 : for Index := 1 to 3 do Write(' Boo'); 1 : if TRUE then begin Write(' TRUE is True,'); Write(' and this is dumb.'); end; else Write(' This number is not in the allowable list'); end; (* of case structure *) Writeln; end; (* of Count loop *) end. (* of main program *)
{ Result of execution 1 2 3 4 5 6 7 8 9 10 } TRUE is true, and this is dumb. Small even number. Boo Boo Boo Small even number. This number is not in the allowable list Small even number. Big number Big number Big number This number is not in the allowable list
This brings us to the end of chapter 4 and you now have enough information to write essentially any program desired in Pascal. You would find however, that you would have a few difficulties if you attempted to try to write a very big program without the topics coming up in the next few chapters. The additional topics will greatly add to the flexibility of Pascal and will greatly ease programming with it.
PROGRAMMING EXERCISES
1. Write a program that lists the numbers from 1 to 12 and writes a special message beside the number representing your month of birth. Write a program that lists all of the numbers from 1 to 12 except for the numbers 2 and 9.
2.
procedure Write_A_Header; begin Writeln('This is the header'); end; procedure Write_A_Message; begin Writeln('This is the message and the count is',Count:4); end; procedure Write_An_Ending; begin Writeln('This is the ending message'); end; begin (* main program *) Write_A_Header; for Count := 1 to 8 do Write_A_Message; Write_An_Ending; end. (* of main program *)
{ Result of execution This This This This This This This This This This } is is is is is is is is is is the the the the the the the the the the header message and the message and the message and the message and the message and the message and the message and the message and the ending message
is is is is is is is is
1 2 3 4 5 6 7 8
(* Chapter 5 - Program 2 *) program Another_Procedure_Example; var Count : integer; Index : integer; procedure Print_Data_Out(Puppy : integer); begin Writeln('This is the print routine',Puppy:5); Puppy := 12; end; procedure Print_And_Modify(var Cat : integer); begin Writeln('This is the print and modify routine',Cat:5); Cat := 35; end; begin (* main program *) for Count := 1 to 3 do begin Index := Count; Print_Data_Out(Index); Writeln('Back from the print routine, Index =',Index:5); Print_And_Modify(Index); Writeln('Back from the modify routine, Index =',Index:5); Print_Data_Out(Index); Writeln('Back from print again and the Index =',Index:5); Writeln; (* This is just for formatting *) end; end. (* of main program *)
{ Result of execution This Back This Back This Back This Back This Back This Back This Back This Back This Back } is the print routine 1 from the print routine, Index = is the print and modify routine from the modify routine, Index = is the print routine 35 from print again and the Index = is the print routine 2 from the print routine, Index = is the print and modify routine from the modify routine, Index = is the print routine 35 from print again and the Index = is the print routine 3 from the print routine, Index = is the print and modify routine from the modify routine, Index = is the print routine 35 from print again and the Index =
1 1 35 35
2 2 35 35
3 3 35 35
We are in a loop in which Count is incremented from 1 to 3 and Pascal does not allow us to modify the loop variable so we make a copy of the value in line 21 and call it Index. We can then modify Index in the main program if we desire.
CALL BY VALUE
In line 7, the procedure heading does not contain the reserved word var in front of the passed parameter and therefore the parameter passing is only one way because of the way Pascal is defined. Without the reserved word var in front of the variable Puppy, the system makes a copy of Index, and passes the copy to the procedure which can do anything with it, using its new name, Puppy, but when control returns to the main program, the original value of Index is still there. The copy of Index named Puppy is modified in the procedure, but the original variable named Index remains unchanged. You can think of the passed parameter without the var as one way parameter passing. This is a "call by value" because only the value of the actual variable is passed to the procedure.
CALL BY REFERENCE
In line 13, the second procedure has the reserved word var in front of its desired name for the variable, namely Cat, so it can not only receive the variable, it can modify it, and return the modified value to the main program. A copy is not made, but the original variable named Index is actually passed to this procedure and the procedure can modify it, therefore communicating with the main program. The formal parameter name, Cat in the procedure, is actually another name for the actual variable named Index in the main program. A passed parameter with a var in front of it is therefore a two way situation. This is a "call by reference" since a reference to the original variable is passed to the procedure.
(* Chapter 5 - Program 3 *) program Make_A_Fruit_Salad; var Apple,Orange,Pear,Fruit : integer; procedure Add_The_Fruit(Value1,Value2 : integer; (* one-way *) var Total : integer; (* two-way *) Value3 : integer); (* one-way *) begin Total := Value1 + Value2 + Value3; end; begin (* main program *) Apple := 4; Orange := 5; Pear := 7; Add_The_Fruit(Apple,Orange,Fruit,Pear); Writeln('The fruit basket contains ',Fruit:3,' fruits'); end. (* of main program *)
{ Result of execution In main program Count = In Print_Some_Data Count = In main program Count = In main program Count = In Print_Some_Data Count = In main program Count = In main program Count = In Print_Some_Data Count = In main program Count = } 1 Index = 7 Index = 1 Index = 2 Index = 7 Index = 2 Index = 3 Index = 7 Index = 3 Index = 1 1 1 2 2 2 3 3 3
The variable Index is defined only in the main program var block and is valid anywhere within the entire Pascal program, including the procedures and is said to be a global variable. The variable Count is also defined in the main program var block and is valid anywhere within the entire Pascal program, except within the procedure where another variable is defined with the same name Count. The two variables with the same name are in fact, two completely different variables, one being available only outside of the procedure and the other being available only within the procedure. The variable More_Stuff is defined within the procedure, so it is invisible to the main program, since it is defined at a lower level than that of the main program. It is only available for use within the procedure in which it is defined. Any variable is available at any point in the program following its definition but only at the level of definition or below. This means that any procedure in the Declaration Part of a program can use any variable defined in the Declaration Part of the program provided that the definition occurs prior to the procedure. Any variable defined within a procedure cannot be used by the main program since the definition is at a lower level than the main program.
Be sure to compile and run PROCED4.PAS before continuing on to the next example program.
{ Result of execution This is procedure one This is procedure one This is procedure two This is procedure one This is procedure two This is procedure three }
Now that you have a good working knowledge of procedures, we need to make another important point. Remember that any Pascal program is made up of three parts, the Program Heading, the Declaration Part, and the Statement Part. The Declaration Part is composed of five unique components, four of which we will discuss in detail in the next chapter, and the last component, which is composed of some number of procedures and functions. We will cover functions in the next example, so for now simply accept the fact that it is like a procedure. A procedure is also composed of three parts, a Procedure Heading, a Declaration Part, and a Statement Part. A procedure, by definition, is therefore nothing more or less than another complete Pascal program embedded within the main program, and any number of procedures can be located in the Declaration Part of the main program. These procedures are all in a line, one right after another.
Since a procedure is defined like the main program, it would seem to be possible to embed another procedure within the Declaration Part of any procedure. This is perfectly valid and is often done, but remember that the embedded procedure can only be called by the procedure in which it is embedded, not by the main program. This is a form of information hiding which is becoming popular in modern software engineering. The previous paragraph is probably a bit difficult to grasp. Don't worry about it too much now, as you become proficient as a Pascal programmer, you will very clearly see how embedded procedures are used.
Any call to this function is actually replaced by an integer value upon completion of the call. Therefore in line 14, the function is evaluated and the value returned is used in place of the function call. The value is returned by assigning the return value to the name of the function as illustrated in line 8. The result of the function is therefore assigned to the variable named Feet. Note that a function always returns a value and it may return additional values if some of its formal parameters are defined as "call by reference". Be sure to compile and run this program.
One of the great mysteries of Pascal and several other popular programming languages, is the recursion of procedure calls. Simply defined, recursion is the ability of a procedure to call itself. Examine the Pascal example file RECURSON.PAS for an example of recursion. The main program is very simple, it sets the variable named Count to the value 7 and calls the procedure Print_And_Decrement. The procedure prefers to refer to the variable by the name Index but that poses no problem for us because we understand that the name of the formal parameter can be any legal identifier. The procedure writes a line to the video display with the value of Index written within the line, and decrements the variable.
(* Chapter 5 - Program 7 *) program Try_Recursion; var Count : integer; procedure Print_And_Decrement(Index : integer); begin Writeln('The value of the index is ',Index:3); Index := Index - 1; if Index > 0 then Print_And_Decrement(Index); end; begin (* main program *) Count := 7; Print_And_Decrement(Count); end. (* main program *)
{ Result of execution The The The The The The The } value value value value value value value of of of of of of of the the the the the the the index index index index index index index is is is is is is is 7 6 5 4 3 2 1
The if statement introduces the interesting part of this program. If the variable is greater than zero, and it is now 6, then the procedure Print_And_Decrement is called once again. This might seem to create a problem except for the fact that this is perfectly legal in Pascal. Upon entering the procedure the second time, the value of Index is printed as 6, and it is once again decremented. Since it is now 5, the same procedure will be called again, and it will continue until the value of Index is reduced to zero when each procedure call will be completed one at a time and control will return to the main program.
program, like the last, is a very stupid way to count from 7 to 0, but it is the simplest program possible with the forward reference.
(* Chapter 5 - Program 8 *) program Forward_Reference_Example; var Number_Of_Times : integer; procedure Write_A_Line(var Count : integer); forward; procedure Decrement(var Index : integer); begin Index := Index - 1; if Index > 0 then Write_A_Line(Index); end; procedure Write_A_Line; begin Writeln('The value of the count is now ',Count:4); Decrement(Count); end; begin (* main program *) Number_Of_Times := 7; Decrement(Number_Of_Times); Writeln; Number_Of_Times := 7; Write_A_Line(Number_Of_Times); end. (* of main program *)
{ Result of execution The The The The The The The The The The The The The } value value value value value value value value value value value value value of of of of of of of of of of of of of the the the the the the the the the the the the the count count count count count count count count count count count count count is is is is is is is is is is is is is now now now now now now now now now now now now now 6 5 4 3 2 1 7 6 5 4 3 2 1
The first procedure, Write_A_Line, has its header defined in exactly the same manner as any other procedure but instead of the normal procedure body, only the reserved word forward is given in line 6. This tells the compiler that the procedure will be defined later. The next procedure is defined as usual, then the body of Write_A_Line is given with only the reserved word procedure and the procedure name. The variable reference has been defined earlier. In this way, each of the procedure names are defined before they are called. It would be possible, by using the forward reference in great numbers, to move the main program ahead of all procedure definitions and have the program structured like some other languages. This style of programming would be perfectly legal as far as the compiler is concerned, but the resulting program would be very non-standard and confusing. You would do well to stick with conventional Pascal formatting techniques and use the forward reference sparingly. Be sure you compile and run this program.
type Procedure_Type = procedure(In1, In2, In3 : integer; var Result : integer); var Number1, Number2, Number3 : integer; Final_Result : integer; Do_Math : Procedure_Type;
procedure Add(In1, In2, In3 : integer; var Result : integer); begin Result := In1 + In2 + In3; Writeln('The sum of the numbers is end;
',Result:6);
procedure Mult(In1, In2, In3 : integer; var Result : integer); begin Result := In1 * In2 * In3; Writeln('The product of the numbers is',Result:6); end; procedure Average(In1, In2, In3 : integer; var Result : integer); begin Result := (In1 * In2 * In3) div 3; Writeln('The Average of the numbers is',Result:6); end; begin Number1 := 10; Number2 := 15; Number3 := 20; Do_Math := Add; Do_Math(Number1, Number2, Number3, Final_Result); Do_Math := Mult; Do_Math(Number1, Number2, Number3, Final_Result); Do_Math := Average; Do_Math(Number1, Number2, Number3, Final_Result); end.
{ Result of execution The sum of the numbers is The product of the numbers is The average of the numbers is } 45 3000 1000
Note the comments in lines 4 and 5 of the program. When using a procedure type or a function type, which is the topic of the next example program, TURBO Pascal requires that you use the compiler directive F+, which forces the system to use far calls for all procedure calls. Study the documentation for your version of TURBO Pascal to obtain more information on compiler directives. Examine the program named FUNCTYPE.PAS for an example of a program using some of the same techniques as the last program but instead uses a function type for the subprogram variable. This program should be simple for you to study on your own concerning the details of operation.
(* Chapter 5 - Program 10 *) program Function_Type_Example; {$F+} (* This forces far calls and is required by TURBO *) (* Pascal to use a function type. *)
type Function_Type = function(In1, In2, In3 : integer) : integer; var Number1, Number2, Number3 : integer; Final_Result : integer; Do_Math : Function_Type;
function Add(In1, In2, In3 : integer) : integer; var Temp : integer; begin Temp := In1 + In2 + In3; Writeln('The sum of the numbers is ',Temp:6); Add := Temp; end; function Mult(In1, In2, In3 : integer) : integer; var Temp : integer; begin Temp := In1 * In2 * In3; Writeln('The product of the numbers is',Temp:6); Mult := Temp; end; function Average(In1, In2, In3 : integer) : integer; var Temp : integer; begin Temp := (In1 * In2 * In3) div 3; Writeln('The Average of the numbers is',Temp:6); Average := Temp; end; begin Number1 := 10; Number2 := 15; Number3 := 20; Do_Math := Add; Final_Result := Do_Math(Number1, Number2, Number3); Do_Math := Mult; Final_Result := Do_Math(Number1, Number2, Number3); Do_Math := Average; Final_Result := Do_Math(Number1, Number2, Number3); end.
{ Result of execution The sum of the numbers is The product of the numbers is The average of the numbers is } 45 3000 1000
The only rule concerning the procedure and function types which must be stated, is that a subprogram type variable can only be assigned subprogram names if the list of actual parameters are identical for the type and the subprogram. This includes the type of the return value for a function. Since this is a new extension to the TURBO Pascal languages, it has not been used much, so don't worry about it too much. You should know that it can be done, because someday you will find a piece of Pascal code with this construct used. Of course, you will someday find a good use for it yourself.
PROGRAMMING EXERCISES
1. Write a program to write your name, address, and phone number with each Writeln in a different procedure. Add a statement to the procedure in RECURSON.PAS to display the value of Index after the call to itself so you can see the value increasing as the recurring calls are returned to the next higher level. Rewrite TEMPCONV.PAS putting the centigrade to Fahrenheit formulas in a function call.
2.
3.
{ Result of execution This is the first program with an array automobile automobile automobile automobile automobile automobile automobile automobile automobile automobile automobile automobile number 1 has the number 2 has the number 3 has the number 4 has the number 5 has the number 6 has the number 7 has the number 8 has the number 9 has the number 10 has the number 11 has the number 12 has the value value value value value value value value value value value value 11 12 13 14 15 16 17 18 19 20 21 22
End of program }
(* Chapter 6 - Program 2 *) program Multiple_Arrays; var Index,Count Checkerboard Value : integer; : array[1..8] of array[1..8] of integer; : array[1..8,1..8] of integer;
begin (* Main program *) for Index := 1 to 8 do begin (* index loop *) for Count := 1 to 8 do begin Checkerboard[Index,Count] := Index + 3*Count; Value[Index,Count] := Index + 2*Checkerboard[Index,Count]; end; end; (* of index loop *) Writeln(' Output of checkerboard'); Writeln; for Index := 1 to 8 do begin for Count := 1 to 8 do Write(Checkerboard[Index,Count]:7); Writeln; end; Value[3,5] := -1; (* change some of the value matrix *) Value[3,6] := 3; Value[Value[3,6],7] := 2; (* This is the same as writing Value[3,7] := 2; *) for Count := 1 to 3 do Writeln; (* Three blank lines *) Writeln('Output of value'); Writeln; for Count := 1 to 8 do begin for Index := 1 to 8 do Write(Value[Count,Index]:7); Writeln; end; end. (* of main program *)
Output of value 9 12 15 18 21 24 27 30 } 15 18 21 24 27 30 33 36 21 24 27 30 33 36 39 42 27 30 33 36 39 42 45 48 33 36 -1 42 45 48 51 54 39 42 3 48 51 54 57 60 45 48 2 54 57 60 63 66 51 54 57 60 63 66 69 72
begin (* main program *) for Index := 1 to 12 do for Counter := 1 to 6 do begin Puppies[Index,Counter] := TRUE; Kitties[Index,Counter] := Puppies[Index,Counter]; end; Writeln(Puppies[2,3]:7,Kitties[12,5]:7,Puppies[1,1]:7); end. (* of main program *)
Some of these types are used in the var declaration part of the program. Notice that since Airplane is an array of Dog_Food and Dog_Food is in turn an array of boolean, then Airplane defines a doubly subscripted array, each element being a boolean variable. This does not define any variables, only a user defined type, which can be used in a var to define a matrix of boolean variables. This is in fact done in the definition of Puppies, which is an array composed of 72 (6 times 12) boolean variables. In the same manner, Stuff is composed of an array of 14 variables, each being an integer variable. The elements of the array are, Stuff[12], Stuff[13], .. Stuff[25]. Notice also that Stuff2 is also defined in exactly the same manner and is also composed of 14 variables. Careful inspection will reveal that Kitties is a variable which has the same definition as Puppies. It would probably be poor programming practice to define them in different manners unless they were in fact totally disassociated. In this example program, it serves to illustrate some of the ways userdefined types can be defined. Be sure to compile and run this program.
(* Chapter 6 - Program 4 *) program Example_Of_Constants; const Max_Size = 12; (* Pascal assumes this is a byte type, but it can be used as an integer also *) Index_Start : integer = 49; (* This is a typed constant *) Check_It_Out : boolean = TRUE; (* Another typed constant *)
type Bigarray = array[1..Max_Size] of integer; Chararray = array[1..Max_Size] of char; var Airplane Seaplane Helicopter Cows Horses Index : : : : : : Bigarray; Bigarray; Bigarray; Chararray; Chararray; integer;
begin (* main program *) for Index := 1 to Max_Size do begin Airplane[Index] := Index*2; Seaplane[Index] := Index*3 + 7; Helicopter[Max_Size - Index + 1] := Index + Airplane[Index]; Horses[Index] := 'X'; Cows[Index] := 'R'; end; end. (* of main program *)
TURBO Pascal has an extension for labels. Any valid identifier, such as used for variables, can be used as a label in addition to the values from 0 to 9999. These are illustrated in the example program. When you compile and run this program, the output will look a little better than the program does.
(* Chapter 6 - Program 5 *) program Label_Illustration; label 274,Repeat_Loop,Help,Dog; var Counter : byte; (* This limits us to a maximum of 255 *)
begin Writeln('Start here and go to "help"'); goto Help; Dog: Writeln('Now go and end this silly program'); goto 274; Repeat_Loop: for Counter := 1 to 4 do Writeln('In the repeat loop'); goto Dog; Help: Writeln('This is the help section that does nothing'); goto Repeat_Loop; 274: Writeln('This is the end of this spaghetti code'); end.
{ Result of execution Start here and go to "help" This is the help section that does nothing In the repeat loop In the repeat loop In the repeat loop In the repeat loop Now go and end this silly program This is the end of this spaghetti code }
PROGRAMMING EXERCISES
1. Write a program to store the integers 201 to 212 in an array then display them on the monitor. 2. Write a program to store a 10 by 10 array containing the products of the indices, therefore a multiplication table. Display the matrix on the video monitor. 3. Modify the program in 2 above to include a constant so that by simply changing the constant, the size of the matrix and the range of the table will be changed.
begin (* main program *) First_Name := 'John '; Initial := 'Q'; Last_Name := 'Doe '; Writeln(First_Name,Initial,Last_Name); for Index := 1 to 10 do Full_Name[Index] := First_Name[Index]; Full_Name[11] := Initial; for Index := 1 to 12 do Full_Name[Index + 11] := Last_Name[Index]; for Index := 24 to 25 do Full_Name[Index] := ' '; Writeln(Full_Name); end. (* main program *)
illustrated in this program. We will see next that TURBO Pascal really shines when it is desired to manipulate text. Compile and run STRARRAY.PAS and observe the output.
(* Chapter 7 - Program 3 *) program What_Is_In_A_String; var First_Name Initial Last_Name Full_Name Index,Total : : : : : string[10]; char; string[12]; string[25]; integer;
begin (* main program *) First_Name := 'John'; Initial := 'Q'; Last_Name := 'Doe'; Writeln(First_Name,Initial,Last_Name); Full_Name := First_Name + ' ' + Initial + ' ' + Last_Name; Writeln(Full_Name); Total := Length(Full_Name); Writeln('The string contains ',Total:4,' characters'); for Index := 1 to Length(Full_Name) do Writeln(Full_Name[Index]); Writeln('End of program'); end. (* main program *)
{ Result of execution JohnQDoe John Q Doe The string contains J o h n Q D o e End of program }
10 characters
PROGRAMMING EXERCISE
1. Write a program in which you store your first, middle, and last names as variables, then display them one to a line. Concatenate the names with blanks between them and display your full name as a single variable.
begin (* main program *) Writeln('Pay rate table':33); Writeln; Write(' DAY Morning Afternoon'); Writeln(' Evening Night'); Writeln; Regular_Rate := 12.00; Evening_Premium := 1.10; Night_Premium := 1.33; Weekend_Premium := 1.25; (* (* (* (* This is the normal pay rate *) 10 percent extra for working late *) 33 percent extra for graveyard *) 25 percent extra for weekends *)
for Day := Mon to Sun do begin case Day of Mon : Write('Monday '); Tue : Write('Tuesday '); Wed : Write('Wednesday'); Thu : Write('Thursday '); Fri : Write('Friday '); Sat : Write('Saturday '); Sun : Write('Sunday '); end; (* of case statement *) for Time := Morning to Night do begin case Time of Morning : Total_Pay := Regular_Rate; Afternoon : Total_Pay := Regular_Rate; Evening : Total_Pay := Regular_Rate * Evening_Premium; Night : Total_Pay := Regular_Rate * Night_Premium; end; (* of case statement *) case Day of Sat : Total_Pay := Total_Pay * Weekend_Premium; Sun : Total_Pay := Total_Pay * Weekend_Premium; end; (* of case statement *) Write(Total_Pay:10:2); end; (* of "for Time" loop *) Writeln; end; (* of "for Day" loop *) end. (* of main program *)
{ Result of execution Pay rate table DAY Monday Tuesday Wednesday Thursday Friday Saturday Sunday } Morning 12.00 12.00 12.00 12.00 12.00 15.00 15.00 Afternoon 12.00 12.00 12.00 12.00 12.00 15.00 15.00 Evening 13.20 13.20 13.20 13.20 13.20 16.50 16.50 Night 15.96 15.96 15.96 15.96 15.96 19.95 19.95
Internally, Pascal does not actually assign the value Mon to the variable Day, but it uses an integer representation for each of the names. This is important to understand because you need to realise that you cannot print out Mon, Tue, etc., but can only use them for indexing control statements. The second line of the type definition defines Time_Of_Day as another scalar which can have any of four different values, namely those listed. The variable Time can only be assigned one of four values since it is defined as the type Time_Of_Day. It should be clear that even though it can be assigned Morning, it cannot be assigned Morning_time or any other variant spelling of Morning, since it is simply another identifier which must have an exact spelling to be understood by the compiler. Several real variables are defined to allow us to demonstrate the use of the scalar variables. After writing a header in lines 16 through 20, the real variables are initialised to some values that are probably not real life values, but will serve to illustrate the scalar variable.
carefully written program will never attempt that, and it would be an indication that something is wrong with either the program or the data. This is one of the advantages of Pascal over older languages and is a reason for the relatively strong type checking built into the language. Further examination will reveal that Index is assigned the range of integers from 1 through 12. During execution of the program, if an attempt is made to assign Index any value outside of that range, a run time error will be generated. Suppose the variable Index was intended to refer to your employees, and you have only 12. If an attempt was made to refer to employee number 27, or employee number -8, there is clearly an error somewhere in the data and you would want to stop running the payroll to fix the problem. Pascal would have saved you a lot of grief.
(* Chapter 8 - Program 2 *) program Scaler_Operations; type Days = (Mon,Tue,Wed,Thu,Fri,Sat,Sun); Work = Mon..Fri; Rest = Sat..Sun; var Day Workday Weekend Index Alphabet Start : : : : : : Days; (* This is any day of the week *) Work; (* These are the the working days *) Rest; (* The two weekend days only *) 1..12; 'a'..'z'; 'a'..'e';
begin (* main program *) (* The following statements are commented out because they contain various errors that will halt compilation. Workday := Sat; Rest := Fri; Index := 13; Index := -1; Alphabet := 'A' Start := 'h' Sat is not part of Workday's subrange. Fri is not part of Weekend's subrange. Index is only allowed to go up to 12, and down to 1. Alphabet, as defined, includes only the lower case alphabet. h is not in the first five letters. *)
End of commented out section. Workday := Tue; Weekend := Sat; Day := Workday; Day := Weekend; Index := 3+2*2; Start := 'd'; Alphabet := Start;
(* since Alphabet is "d" Start := Succ(Alphabet); (* Start will be 'e' Start := Pred(Alphabet); (* Start will be 'c' Day := Wed; Day := Succ(Day); (* Day will now be 'Thu' *) Day := Succ(Day); (* Day will now be 'Fri' *) Index := Ord(Day); (* Index will be 4 (Fri = 4) *) end. (* of main program *)
*) *) *)
out until you are ready to include it with the rest of the code. The errors are self explanatory and it would pay for you to spend enough time to understand each of the errors. There are seven assignment statements as examples of sub-range variable use in lines 29 through 35. Notice that the variable Day can always be assigned the value of either Workday or Weekend, but the reverse is not true because Day can assume values that would be illegal to assign to the others.
SETS
Now for a new topic, sets. Examining the example Pascal program SETS.PAS will reveal some sets. A scalar variable is defined first, in this case the scalar type named Goodies. A set is then defined with the reserved words set of followed by a predefined scalar type. Several variables are defined as sets of Treat, after which they can individually be assigned portions of the entire set.
Consider the variable Ice_Cream_Cone which has been defined as a set of type Treat. This variable is composed of as many elements of Goodies as we care to assign to it. In the program, we define it as being composed of Ice_Cream, and Cone. The set Ice_Cream_Cone is therefore composed of two elements, and it has no numerical or alphabetic value as most other variables have. In lines 21 through 26, you will see four more delicious deserts defined as sets of their components. Notice that the banana split is first defined as a range of terms, then another term is added to the group illustrating how you can add to a set. All five are combined in the set named Mixed, then Mixed is subtracted from the entire set of values to form the set of ingredients that are not used in any of the deserts. Each ingredient is then checked to see if it is in the set of unused ingredients, and printed out if it is. Note that in is another reserved word in Pascal. Running the program will reveal a list of unused elements. In this example, better programming practice would have dictated defining a new variable, possibly called Remaining for the ingredients unused in line 32. It was desirable to illustrate that Mixed
could be assigned a value based on subtracting itself from the entire set, so the poor variable name was used. When you compile and run this program you will see that this example results in some nonsense results but hopefully it led your thinking toward the fact that sets can be used for inventory control, possibly a parts allocation scheme, or some other useful system.
(* Chapter 8 - Program 3 *) program Define_Some_Sets; type Goodies = (Ice_Cream,Whipped_Cream,Banana,Nuts,Cherry, Choc_Syrup,Strawberries,Caramel,Soda_Water, Salt,Pepper,Cone,Straw,Spoon,Stick); Treat = set of Goodies; var Sundae Banana_Split Soda Ice_Cream_Cone Nutty_Buddy Mixed Index : : : : : : : Treat; Treat; Treat; Treat; Treat; Treat; byte;
begin (* define all ingredients used in each treat *) Ice_Cream_Cone := [Ice_Cream,Cone]; Soda := [Straw,Soda_Water,Ice_Cream,Cherry]; Banana_Split := [Ice_Cream..Caramel]; Banana_Split := Banana_Split + [Spoon]; Nutty_Buddy := [Cone,Ice_Cream,Choc_Syrup,Nuts]; Sundae := [Ice_Cream,Whipped_Cream,Nuts,Cherry,Choc_Syrup, Spoon]; (* combine for a list of all ingredients used *) Mixed := Ice_Cream_Cone + Soda + Banana_Split + Nutty_Buddy + Sundae; Mixed := [Ice_Cream..Stick] - Mixed; (* all ingredients not used *) if if if if if if if if if if if if if if if end. Ice_Cream Whipped_Cream Banana Nuts Cherry Choc_Syrup Strawberries Caramel Soda_Water Salt Pepper Cone Straw Spoon Stick in in in in in in in in in in in in in in in Mixed Mixed Mixed Mixed Mixed Mixed Mixed Mixed Mixed Mixed Mixed Mixed Mixed Mixed Mixed then then then then then then then then then then then then then then then Writeln('Ice cream not used'); Writeln('Whipped cream not used'); Writeln('Bananas not used'); Writeln('Nuts are not used'); Writeln('Cherrys not used'); Writeln('Chocolate syrup not used'); Writeln('Strawberries not used'); Writeln('Caramel is not used'); Writeln('Soda water is not used'); Writeln('Salt not used'); Writeln('Pepper not used'); Writeln('Cone not used'); Writeln('Straw not used'); Writeln('Spoon not used'); Writeln('Stick not used');
{ Result of execution Salt not used Pepper not used Stick not used }
begin (* main program *) Data_Set := []; Print_Group := ''; Storage := 'This is a set test.'; for Index := 1 to Length(Storage) do begin if Storage[Index] in ['a'..'z'] then begin if Storage[Index] in Data_Set then Writeln(Index:4,' ',Storage[Index], ' is already in the set') else begin Data_Set := Data_Set + [Storage[Index]]; Print_Group := Print_Group + Storage[Index]; Writeln(Index:4,' ',Storage[Index], ' added to group, complete group = ', Print_Group); end; end else Writeln(Index:4,' ',Storage[Index], ' is not a lower case letter'); end; end. (* of main program *)
{ Result of execution 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 T h i s i s a s e t t is not a lower case letter added to group, complete group added to group, complete group added to group, complete group is not a lower case letter is already in the set is already in the set is not a lower case letter added to group, complete group is not a lower case letter is already in the set added to group, complete group added to group, complete group is not a lower case letter is already in the set
= h = hi = his
= hisa
= hisae = hiseat
16 17 18 19 }
e s t .
is is is is
already in the set already in the set already in the set not a lower case letter
PROGRAMMING EXERCISE
1. Modify FINDCHRS.PAS to search for upper-case letters.
Chapter 9 : RECORDS
A VERY SIMPLE RECORD
We come to the grandaddy of all data structures in Pascal, the record. A record is composed of a number of variables any of which can be of any predefined data type, including other records. Rather than spend time trying to define a record in detail, lets go right to the first example program, SMALLREC.PAS. This is a program using nonsense data that will illustrate the use of a record.
(* Chapter 9 - Program 1 *) program A_Small_Record; type Description Year : Model : Engine : end; var = record integer; string[20]; string[8];
begin
Truck.Year := 1988; Truck.Model := 'Pickup'; Truck.Engine := 'Diesel'; for Index := 1 to 10 do begin Cars[Index].Year := 1930 + Index; Cars[Index].Model := 'Duesenburg'; Cars[Index].Engine := 'V8'; end; Cars[2].Model := 'Stanley Steamer'; Cars[2].Engine := 'Coal'; Cars[7].Engine := 'V12'; Cars[9].Model := 'Ford'; Cars[9].Engine := 'rusted'; Write('My ',Truck.Year:4,' '); Write(Truck.Model,' has a '); Writeln(Truck.Engine,' engine.'); for Index := 1 to 10 do begin Write('My ',Cars[Index].Year:4,' '); Write(Cars[Index].Model,' has a '); Writeln(Cars[Index].Engine,' engine.'); end; end. (* of main program *)
{ Result of execution My My My My My My My My My My My } 1988 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 Pickup has a Diesel engine. Duesenburg has a V8 engine. Stanley Steamer has a Coal engine. Duesenburg has a V8 engine. Duesenburg has a V8 engine. Duesenburg has a V8 engine. Duesenburg has a V8 engine. Duesenburg has a V12 engine. Duesenburg has a V8 engine. Ford has a rusted engine. Duesenburg has a V8 engine.
There is only one entry in the type declaration part of the program, the record identified by the name Description. The record is composed of three fields, the Year, Model, and Engine variables. Notice that the three fields are each of a different type, indicating that the record can be of mixed types. You have a complete example of the way a record is defined before you. It is composed of the identifier Description, the = sign, the reserved word record, the list of elements, and followed by the reserved word end. This is one of the places in Pascal where an end is used without a corresponding begin. Notice that this only defines a type, it does not define any variables. That is done in the var declaration where the variable Truck is defined as a record of type Description and Cars is defined to have 10 complete records of the type Description. The variable Truck has three components, Year, Model, and Engine, and any or all of these components can be used to store data pertaining to Truck. When assigning data to the variable Truck, for example, there are actually three parts to the variable, so we use three assignment statements, one for each of the sub-fields. In order to assign values to the various sub-fields, the variable name is followed by the sub-field name with a separating period. The "var.sub_field" combination is a variable name. Keep in mind that Truck is a complete record containing three variables, and to assign or use one of the variables, you must designate which sub-field you are interested in. Examine lines 16 through 18 of the program where the three fields are assigned meaningless data for illustration. The Year field is assigned an integer number, the Model field is assigned the name Pickup, and the Engine variable is assigned the value Diesel. A loop is then used to assign data to all 10 records of Cars. In order to further illustrate that there are actually 30 variables in use here, a few are changed at random in lines 26 through 30, being very careful to maintain the required types as defined in the type declaration part of the program. Finally, all ten composite variables, consisting of 30 actual variables in a logical grouping are printed out using the same "var.sub-field" notation described above. If the preceding description of a record is not clear in your mind, review it very carefully. It's a very important concept in Pascal, and you won't have a hope of a chance of understanding the next example until this one is clear. Be sure to compile and run SMALLREC.PAS so you can study the output.
A SUPER RECORD
Examine the Pascal example file BIGREC.PAS for a very interesting record. First we have a constant defined. Ignore it for the moment, we will come back to it later. Within the type declaration we have three records defined, and upon close examination, you will notice that the first two records are included as part of the definition of the third record. The record identified as Person, actually contains 9 variable definitions, three within the Full_Name record, three of its own, and three within the Date record. Once again, this is a type declaration and does not actually define any variables, that is done in the var part of the program. The var part of the program defines some variables beginning with the array of Friend containing 50 (because of the constant definition in the const part) records of the user defined type, Person. Since the type Person defines 9 fields, we have now defined 9 times 50 = 450 separate and distinct variables, each with its own defined type. Remember that Pascal is picky about assigning data by the correct type. Each of the 450 separate variables has its own type associated with it, and the compiler will generate an error if you try to assign any of those variables the wrong type of data. Since Person is a type definition, it can be used to define more than one variable, and in fact it is used again to define three more records, Self, Mother, and Father. These three records are each composed of 9 variables, so we have 27 more variables which we can manipulate within the program. Finally we have the variable Index defined as a simple byte type variable.
(* Chapter 9 - Program 2 *) program A_Larger_Record; const Number_Of_Friends = 50; record : string[12]; : char; : string[15];
type Full_Name = First_Name Initial Last_Name end; Date Day Month Year end;
Person = record Name : Full_Name; City : string[15]; State : string[2]; Zipcode : string[5]; Birthday : Date; end; var Friend : array[1..Number_Of_Friends] of Person; Self,Mother,Father : Person; Index : byte;
begin (* main program *) Self.Name.First_Name := 'Charley'; Self.Name.Initial := 'Z'; Self.Name.Last_Name := 'Brown'; with Self do begin City := 'Anywhere'; State := 'CA'; Zipcode := '97342'; Birthday.Day := 17; with Birthday do begin Month := 7; Year := 1938; end; end; (* all data for self now defined *) Mother := Self; Father := Mother; for Index := 1 to Number_Of_Friends do Friend[Index] := Mother; Write(Friend[27].Name.First_Name,' '); Write(Friend[33].Name.Initial,' '); Write(Father.Name.Last_Name); Writeln; end. (* of main program *)
first name of Self and is used in the assignment statement in line 31 where it is assigned the name of "Charley". The next two fields are handled in the same way and are self explanatory.
SUPER-ASSIGNMENT STATEMENTS
The statement in line 45, "Mother := Self;" is very interesting. Since both of these are records, both are the same type of record, and both therefore contain 9 variables, Pascal is smart enough to recognise that, and assign all nine values contained in Self to the corresponding variables of Mother. So after one statement, the record variable Mother is completely defined. The statement in line 46 assigns the same values to the nine respective variables of Father, and the next two lines assign all 50 Friend variables the same data. By this point in the program, we have therefore generated 450 + 27 = 477 separate pieces of data so far in this program. We could print it all out, but since it is nonsense data, it would only waste time and paper. Lines 49 through 52 write out three sample pieces of the data for your inspection.
how big this database can become on your computer. If you are using TURBO Pascal, you will be limited to slightly more than 1000 because of the 64K limitation of an executable program, and the fact that all of this data is stored within that 64K boundary. It should be noted that TURBO Pascal allows a program larger than 64K but still places a limitation of 64K on each compilation unit. See how big you can make the number of friends before you get the memory overflow message. Keep the number in mind because when we get to the chapter on Pointers and Dynamic Allocation, you will see a marked increase in allowable size, especially if you have a large amount of RAM installed in your computer.
A VARIANT RECORD
If any part of this chapter is still unclear, it would be good for you to go back and review it at this time. The next example will really tax your mind to completely understand it, and this will be true especially if the prior material is not clear. Examine the Pascal program VARREC.PAS for an example of a program with a variant record definition. In this example, we first define a scalar type, namely Kind_Of_Vehicle for use within the record. Then we have a record defining Vehicle, intended to define several different vehicles, each with different kinds of data. It would be possible to define all variables for all types of vehicles, but it would be a waste of storage space to define the number of tires for a boat, or the number of propeller blades used on a car or truck. The variant record lets us define the data precisely for each vehicle without wasting data storage space.
(* Chapter 9 - Program 3 *) program Variant_Record_Example; type Kind_Of_Vehicle = (Car,Truck,Bicycle,Boat); Vehicle = record Owner_Name : string[25]; Gross_Weight : integer; Value : real; case What_Kind : Kind_Of_Vehicle of Car : (Wheels : integer; Engine : string[8]); Truck : (Motor : string[8]; Tires : integer; Payload : integer); Bicycle : (Tyres : integer); Boat : (Prop_Blades : byte; Sail : boolean; Power : string[8]); end; (* of record *) var Sunfish,Ford,Schwinn,Mac : Vehicle; begin (* main program *) Ford.Owner_Name := 'Walter'; (* fields defined in order *) Ford.Gross_Weight := 5750; Ford.Value := 2595.00; Ford.What_Kind := Truck; Ford.Motor := 'V8'; Ford.Tires := 18; Ford.Payload := 12000; with Sunfish do begin What_Kind := Boat; (* fields defined in random order *) Sail := TRUE; Prop_Blades := 3; Power := 'wind'; Gross_Weight := 375; Value := 1300.00; Owner_Name := 'Herman and George'; end; Ford.Engine := 'flathead'; (* tag-field not defined yet but it *) Ford.What_Kind := Car; (* must be before it can be used *) Ford.Wheels := 4; (* notice that the non variant part is not redefined here *)
Mac := Sunfish; (* entire record copied, including the tag-field *) if Ford.What_Kind = Car then (* this should print *) Writeln(Ford.Owner_Name,' owns the car with a ',Ford.Engine, ' engine.'); if Sunfish.What_Kind = Bicycle then (* this should not print *) Writeln('The sunfish is a bicycle which it shouldn''t be'); if Mac.What_Kind = Boat then (* this should print *) Writeln('The mac is now a boat with',Mac.Prop_Blades:2, ' propeller blades.'); end. (* of main program *)
{ Result of execution Walter owns the car with the flathead engine. The mac is now a boat with three propeller blades. }
WHAT IS A TAG-FIELD?
In the record definition we have the usual record header followed by three variables defined in the same manner as the records in the last two example programs. Then we come to the case statement. Following this statement, the record is different for each of the four types defined in the associated scalar definition. The variable What_Kind is called the tag-field and must be defined as a scalar type prior to the record definition. The tag-field is used to select the variant, when the program uses one of the variables of this record type. The tag-field is followed by a colon and its type definition, then the reserved word of. A list of the variants is then given, with each of the variants having the variables for its particular case defined. The list of variables for one variant is called the field list. A few rules are in order at this point. The variants do not have to have the same number of variables in each field list, and in fact, one or more of the variants may have no variables at all in its variant part. If a variant has no variables, it must still be defined with a pair of empty parentheses followed by a semicolon. All variables in the entire variant part must have unique names. The three variables, Wheels, Tires, and Tyres, all mean the same thing to the user, but they must be different for the compiler. You may use the same identifiers again in other records and for simple variables anywhere else in the program. The Pascal compiler can tell which variable you mean by its context. Using the same variable name should be discouraged as bad programming practice because it may confuse you or another person trying to understand your program at a later date. The final rule is that the variant part of the record must be the last part of it, and in fact, the last part of any or all variants can itself have a variant part to it. That is getting pretty advanced for our level of use of Pascal at this time however.
are available for use with the record Ford, but the variables named Wheels, Engine, Tyres, etc. are not available in the record named Ford. Next, we will define the record Sunfish as a Boat, and define all of its variables in lines 33 through 41. All of Sunfish's variables are defined but in a rather random order to illustrate that they need not be defined in a particular order. You should remember the with statement from the last example program. To go even further in randomly assigning the variables to a record, we redefine Ford as having an Engine which it can only have if it is a car. This is one of the fine points of the Pascal record. If you assign any of the variant variables, the record is changed to that variant, but it is the programmers responsibility to assign the correct tag-field to the record, not Pascal's. Good programming practice would be to assign the tag-field before assigning any of the variant variables. The remainder of the Ford variables are assigned to complete that record, the non-variant part remaining from the last assignment. The variable Mac is now set equal to the variable Sunfish in line 48. All variables within the record are copied to Mac including the tag-field, making Mac a Boat.
PROGRAMMING EXERCISE
1. Write a simple program with a record to store the names of five of your friends and display the names.
begin Writeln('Integer'); Index := 17; Writeln(Index,Index); Writeln(Index:15,Index:15); Writeln; Writeln('Real'); Count := 27.5678; Writeln(Count,Count); Writeln(Count:15,Count:15); Writeln(Count:15:2,Count:15:2); Writeln(Count:15:3,Count:15:3); Writeln(Count:15:4,Count:15:4); Writeln; Writeln('Boolean'); What := FALSE; Writeln(What,What); Writeln(What:15,What:15); Writeln('Char'); Letter := 'Z'; Writeln(Letter,Letter); Writeln(Letter:15,Letter:15); Writeln('String'); Name := 'John Doe'; Writeln(Name,Name); Writeln(Name:15,Name:15); Writeln; Writeln('Text output','Text output'); Writeln('Text output':15,'Text output':15); end.
27.57 27.568 27.5678 Boolean FALSEFALSE FALSE Char ZZ Z String John DoeJohn Doe John Doe
FALSE
John Doe
(* Chapter 10 - Program 2 *) program Read_Some_Variables; var Index : byte; Number1,Number2,Number3 : integer; begin (* main program *) Writeln('This is the ''Read'' portion of the program'); for Index := 1 to 5 do begin Write('Enter up to three integers '); Read(Number1,Number2,Number3); Writeln('Thank you'); Writeln('The numbers entered were ',Number1:6,Number2:6, Number3:6); end; Writeln; Writeln('This is the ''Readln'' portion of the program'); for Index := 1 to 5 do begin Write('Enter up to three integers '); Readln(Number1,Number2,Number3); Writeln('Thank you'); Writeln('The numbers entered were ',Number1:6,Number2:6, Number3:6); end; end. (* of main program *)
{ Result of execution (The results are dependent on the data entered at the keyboard) }
The variable Index is used to loop five times through a sequence of statements with one Read statement in it. The three integer values are read in and stored in their respective variables with the one statement. If less than three are entered at the keyboard, only as many as are read in will be defined, the rest will be unchanged. Following completion of the first loop, there is a second loop in lines 19 through 25 that will be executed 5 times with only one minor change, the Read statement is replaced by the Readln statement. At this point it would be best run this program trying several variations with input data. When you run READINT.PAS, it will request three integers. Reply with three small integers of your choice with as many blank spaces between each as you desire, followed by a carriage return. The system will echo your three numbers back out, and request three more. Respond with only one number this time, different from each of the first three, and a carriage return. You will get your new number followed by your previous second and third number indicating that you did not re-enter the last two integer variables. Enter three more numbers, this time including a negative number and observe the echo once again. Continue entering numbers until the system outputs the message indicating that it will now be using the Readln for reading data. At this point enter the same numbers that you did in the previous section and notice the difference, which is only very slight. Each time you hit the enter key to cause the computer to process the data you have just given it, it will echo the carriage return to the display, and the "Thank you" message will be on a new line. When entering data from the keyboard, the only difference in Read and Readln is whether or not the carriage return is echoed to the display following the data read operation. It should not be a surprise to you that after you enter the data, the data is stored within the program and can be used anywhere that integer data is legal for use. Thus, you could read in an integer, and use the integer to control the number of times through a loop, as a case selector, etc.
{ Result of execution (The results depend on the data entered at the keyboard) }
Finally, READSTRG.PAS will also read up to 10 characters, but since a string is a dynamic length variable, it will only print out the characters you input each time, up to the maximum of 10 as defined in the var declaration. It will display trailing blanks if you type them in because blanks are valid characters.
begin for Index := 1 to 5 do begin Write('Input a character '); Readln(Letter); Writeln('The character input was an ',Letter); end; end.
{ Result of execution ( The results depend on the data entered at the keyboard) }
(* Chapter 10 - Program 5 *) program Read_A_String; var Index : byte; Field : string[10]; begin for Index := 1 to 5 do begin Write('Enter up to 10 characters '); Readln(Field); Writeln('The data you entered was (',Field,')'); end; end.
{ Result of execution (The output depends on the data entered at the keyboard) }
program has a few outputs to the monitor in lines 9 and 10 with the device identifier included, namely Output. This is only done to show you the general form of the Write statements, but if you desire, you can add the standard device identifier to every monitor output.
(* Chapter 10 - Program 6 *) program Printout_Example; uses Printer; var Index : byte; begin Writeln(Output,'Printer program example'); Writeln(Output,'Turn on your printer and install paper in it.'); Writeln(Lst,'This is to demonstrate printing in Pascal'); Writeln(Lst); for Index := 1 to 15 do begin Write(Lst,'The index value is '); Write(Lst,Index:3); Writeln(Lst,' at this point'); end; end.
{ Result of execution Printer example program Turn on your printer and install paper in it
(The following is output to the printer) This is to demonstrate printing in Pascal The The The The The The The The The The The The The The The } index index index index index index index index index index index index index index index value value value value value value value value value value value value value value value is is is is is is is is is is is is is is is 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 at at at at at at at at at at at at at at at this this this this this this this this this this this this this this this point point point point point point point point point point point point point point point
There are many statements in this program with the device identifier Lst, which is the standard name for the list device or the printer. It should be obvious to you that the first field is the device selector which is used to direct the output to the desired device. Compile and run this program with your printer turned on for some printer output. Just to supply you with a bit more information, every Read and Readln statement is also required to have a device identifier prior to the first input field. As you may suspect, it is also defaulted to Input if none is specified, and the standard input device is the keyboard.
PROGRAMMING EXERCISE
3.
Write a program containing a loop to read in a character string up to 60 characters long, then print the string on your printer. When you run the program, you will have the simplest word processing program in the world. Be sure to include a test to end the loop, such as when "END" is typed in.
next argument is the filename desired. The filename can be defined as a string constant, as it is here, or as a string variable.
(* Chapter 11 - Program 1 *) program Read_A_File; var Turkey : text; Big_String : string[80]; begin (* main program *) Assign(Turkey,'READFILE.PAS'); Reset(Turkey); while not Eof(Turkey) do begin Readln(Turkey,Big_String); Writeln(Big_String); end; (* of while loop *) Close(Turkey); end. (* of program *)
The text type is a predefined type and is used to define a file identifier. It is predefined as a "file of char", so it can only be used for a text file. We will see later that there is another type of file, a binary file. You will find that the operating system that you are using requires a file name to follow certain conventions when it is named, and the Pascal programming language has a set of rules by which an identifier can be named. Since these two conventions are not necessarily the same, it is necessary to give each file an external name which the operating system is happy with, and an internal filename which Pascal is happy with. It now becomes necessary to tie these two names together, and this is the primary job of the Assign statement. Now that we have a file identified, it is necessary to prepare it for reading by executing a reset statement in line 9. The Reset statement positions the read pointer at the beginning of the file, ready to read the first piece of information in the file. Once we have done that, data is read from the file in the same manner as it was when reading from the keyboard. In this program, the input is controlled by the while loop which is executed until we exhaust the data in the file.
To actually read the data, we use the Readln procedure, giving it our identifier Turkey and the name of the variable we want the data read into. In this case, we read up to 80 characters into the string and if more are available, ignore them. You should remember when we did this in the last chapter from the keyboard input. We are using the same technique here except we are reading from a file this time. Since we would like to do something with the data, we output the line to the default device, the video monitor. It should be clear to you by now that the program will read the entire file and display it on the monitor. Finally, we Close the file Turkey. It is not really necessary to close the file because the system will close it for us automatically at program termination, but it is a good habit to get into. It must be carefully pointed out here, that you did not do anything to the input file, you only read the data and left it intact. You could Reset it and reread it again in this same program. Compile and run this program to see if it does what you expect it to do.
You should be able to understand clearly how each of these operations is accomplished. Compile and run this program, entering any filename we have used so far as the file to be listed on the monitor (be sure to include the .PAS extension). After a successful run, enter a non-existent filename and see the I/O error generated by the Pascal runtime system. The next example program will illustrate a more graceful method of detecting this error.
begin Write('Enter input file name '); Readln(Input_File_Name); Assign(Read_File,Input_File_Name); {$I-} Reset(Read_File); {$I+} Read_File_OK := (IOResult = 0); if Read_File_OK then begin Write('Enter output file name '); Readln(Output_File_Name); Assign(Write_File,Output_File_Name); Rewrite(Write_File); Line_Number := 1; while not Eof(Read_File) do begin Readln(Read_File,Big_String); Write(Write_File,Line_Number:5,' '); Writeln(Write_File,Big_String); Line_Number := Line_Number + 1; end; Close(Read_File); Close(Write_File); end else Writeln('Input file doesn''t exist, execution aborted'); end. (* of program *)
{ Result of execution (The selected file is copied to the selected output file) }
with this package is a text file. One notable exception is the file named LIST.EXE, which is an executable program file.
(* Chapter 11 - Program 4 *) program Read_Integers_From_File; var Cupcake : text; Index : byte; Var1,Var2,Var3,Var4,Var5 : integer; begin Assign(Cupcake,'INTDATA.TXT'); Reset(Cupcake); for Index := 1 to 5 do begin Read(Cupcake,Var1,Var2,Var3); Writeln(Var1:6,Var2:6,Var3:6); end; Reset(Cupcake); Writeln; for Index := 1 to 5 do begin Readln(Cupcake,Var1,Var2,Var3); Writeln(Var1:6,Var2:6,Var3:6); end; Reset(Cupcake); Writeln; for Index := 1 to 5 do begin Read(Cupcake,Var1,Var2,Var3,Var4,Var5); Writeln(Var1:6,Var2:6,Var3:6,Var4:6,Var5:6); end; Reset(Cupcake); Writeln; for Index := 1 to 5 do begin Readln(Cupcake,Var1,Var2,Var3,Var4,Var5); Writeln(Var1:6,Var2:6,Var3:6,Var4:6,Var5:6); end; Close(Cupcake); end. (* of main program *)
{ Result of execution (This data is read from INTDATA.TXT) 101 104 107 110 113 101 105 109 113 117 101 106 111 116 121 101 109 117 125 133 } 102 105 108 111 114 102 106 110 114 118 102 107 112 117 122 102 110 118 126 134 103 106 109 112 115 103 107 111 115 119 103 108 113 118 123 103 111 119 127 135 104 109 114 119 124 104 112 120 128 136 105 110 115 120 125 105 113 121 129 137
The example program has nothing new, you have seen everything in it before. We have an assignment, followed by a reset of our file, followed by four read and write loops. Each of the loops has a subtle difference to illustrate the Read and Readln statements. Notice that the same file is used for reading four times with a Reset prior to each, illustrating the non-destructive read mentioned a few paragraphs ago. The file we will be using is named INTDATA.TXT and is on your disk. You could display it at this time using the program READDISP.PAS we covered recently. Notice that it is simply composed of the integer values from 101 to 148 arranged four to a line with a couple of spaces between each for separation and a neat appearance. The important thing to remember is that there are four data points per line.
(* Chapter 11 - Program 5 *) program Read_Real_Data_From_A_File; var Hot_Dog : text; Index : byte; Line_Number : integer; Real1,Real2,Real3 : real;
begin (* main program *) Assign(Hot_Dog,'REALDATA.TXT'); Reset(Hot_Dog); for Index := 1 to 7 do begin Readln(Hot_Dog,Line_Number,Real1,Real2,Real3); Writeln(Line_Number:7,Real1:12:3,Real2:12:3,Real3:12:3); end; Close(Hot_Dog); end. (* of main program *)
{ Result of execution 1 2 3 4 6 7 8 } 23.600 -0.400 4.000 77.000 1.000 12.120 2.000 145.450 -0.050 3456.000 5.000 2.000 13.110 3.000 234.800 0.345 123.000 -0.003 3.000 14.140 4.000
If a Read were substituted for the Readln in line 14 of the program, the file pointer would not be advanced to the beginning of line 6 after the fourth pass through the loop. The next attempt to read would result in trying to read the value 0.0006 as an integer, and a run time error would result. Modify the program, substituting a Read for the Readln in line 14, and see if this is not true. It will be left as an exercise for the diligent student to add code to detect and act on the error in a manner similar to that illustrated in READSTOR.PAS earlier in this chapter. It should be pointed out that TURBO Pascal requires a digit both before and after the decimal point in all data that is to be read in as real type data or it will be flagged as a run-time error and the program will be halted. The digits can be zero as they are in several places in the example file but they must be there. That is all there is to reading and writing text files. If you learn the necessities, you will not be stumbling around in the area of input/output which is very intimidating to many people. Remember to Assign, then Reset before reading, Rewrite before writing, and Close before quitting. It is of the utmost importance to close a file you have been writing to before quitting to write the last few buffers to the file, but it is not as important to close read files unless you are using a lot of them, as there is an implementation dependent limit of how many files can be open at once. It is possible to read from a file, close it, reopen it, and write to it in one program. You can reuse a file as often as you desire in a program, but you cannot read from and write into a file at the same time.
Output_File is defined as a "file of Dat_Rec", the record defined earlier. The variable Dog_Food is then defined as an array of the record, and a simple variable is defined.
(* Chapter 11 - Program 6 *) program Binary_Output_Example; type Dat_Rec Count Size Name end; = : : : record integer; real; string[30];
var Output_File : file of Dat_Rec; Dog_Food : array[1..20] of Dat_Rec; Index : byte; begin (* main program *) Assign(Output_File,'KIBBLES.BIT'); Rewrite(Output_File); for Index := 1 to 20 do begin Dog_Food[Index].Count := Index; Dog_Food[Index].Size := 12345.6789; Dog_Food[Index].Name := 'Large size Kibbles & Bits'; end; Writeln('Begin outputting data'); for Index := 1 to 20 do Write(Output_File,Dog_Food[Index]); Close(Output_File); Writeln('End of output'); end. (* of main program *)
{ Result of execution Begin outputting data End of output (In addition to the above output to the monitor, the file named KIBBLES.BIT is created and filled with binary data.) }
Any file assigned a type of text, which is a "file of char", is a text file. A text file can be read and modified with a text editor, printed out, displayed on the monitor, etc. If a file is defined with any other definition, it will be a binary file and will be in an internal format as defined by the Pascal compiler and may not be readable by any compiler other than the one used to write it. Attempting to display such a file will result in very strange looking gibberish on the monitor. When we get to the program, the output file is assigned a name in line 15, and a Rewrite is performed on it to reset the input pointer to the beginning of the file, empty the file, and prepare for writing data into it. The loop in lines 18 through 22 simply assigns nonsense data to all of the variables in the 20 records so we have something to work with. We write a message to the display that we are ready to start outputting data, then we output the data one record at a time with the standard Write statement. A few cautions are in order here. The output file can be defined to store any simple variable type, integer, byte, real, or a record, but the types cannot be mixed. The record itself however, can be any combination of data including other records if desired, but any file can only have one type of record written to it. A Writeln statement is illegal when writing to a binary file because a binary file is not line oriented. A Write statement is limited to one output field per statement. This is not a serious limitation since it is a simple matter to put one Write statement in the program for each variable you wish to write out to the file. It is important to Close the file when you are finished writing to it.
PROGRAMMING EXERCISES
1. Modify READFILE.PAS so that after reading and displaying the file, the file is reset, then read and displayed again. This was suggested in the text. Write a program to read the data from any text file, and display it on the monitor with line numbers and the number of characters in each line. Finally display the number of lines found in the file, and the total number of characters in the entire file. Compare this number with the filesize given by the DOS command DIR.
2.
begin Index := 17; Where := Addr(Index); Who := Addr(Index); Writeln('The values are Where^ := 23; Writeln('The values are
',Index:5,Where^:5,Who^:5);
',Index:5,Where^:5,Who^:5);
Pt1 := Addr(Index); Pt2 := Pt1; Pt3 := Pt2; Pt2^ := 15; Writeln('The Pt values are',Pt1^:5,Pt2^:5,Pt3^:5); end.
{ Result of execution The values are The values are The Pt values are } 17 23 15 17 23 15 17 23 15
Display the program named POINT2.PAS on your monitor for an example of another new extension to the Pascal programming language by Borland. This program is identical to the last example program except in lines 13, 14 and 20, where the symbol @ is used to denote the address of the variable Index rather than the function Addr. This was added to TURBO Pascal as a convenience for you. In ANSI standard Pascal the @ symbol is used as a synonym for the ^ symbol but Borland chose to use it for a completely different purpose. Use of this symbol will result in a program that will not compile properly with any Pascal compiler other than TURBO Pascal.
(* Chapter 12 - Program 2 *) program TURBO_4_Pointer_Example; type Int_Point = ^Integer; var Index Where Who Pt1, Pt2, Pt3 : : : : Integer; ^Integer; ^Integer; Int_Point;
begin Index := 17; Where := @Index; Who := @Index; Writeln('The values are Where^ := 23; Writeln('The values are
',Index:5,Where^:5,Who^:5);
',Index:5,Where^:5,Who^:5);
Pt1 := @Index; Pt2 := Pt1; Pt3 := Pt2; Pt2^ := 15; Writeln('The Pt values are',Pt1^:5,Pt2^:5,Pt3^:5); end.
{ Result of execution The values are The values are The Pt values are } 17 23 15 17 23 15 17 23 15
My_Name : ^Name; (* My_Name is a pointer to a string[20] *) My_Age : ^integer; (* My_Age is a pointer to an integer *)
begin New(My_Name); New(My_Age); My_Name^ := 'John Q Doe'; My_Age^ := 27; Writeln('My name is ',My_Name^); Writeln('My age is ',My_Age^:3); Dispose(My_Name); Dispose(My_Age); end.
referenced by using the pointer to it followed by a ^, just like in the last program, and is read, "the variable to which the pointer points". The statement in line 11 assigns a place on the heap to an integer type variable and puts its address in My_Age. The data space can now be pictured as in figure 12-5. Note that we have the data locations defined but there is no data stored in the locations yet. Following the New statements we have two assignment statements in which the two variables pointed at are assigned values compatible with their respective types, and they are both written out to the video display in much the same manner as we did in the program named POINT.PAS. Following execution of lines 13 and 14, the data space is configured as illustrated in figure 12-6. Lines 16 and 17 illustrate that the dynamically allocated data can be used in the same manner as any data provided the "carat" is used with the variable name.
(* Chapter 12 - Program 4 *) program A_Dynamic_Storage_Record; const Number_Of_Friends = 50; record : string[12]; : char; : string[15];
type Full_Name = First_Name Initial Last_Name end; Date Day Month Year end;
Person_Id = ^Person; Person = record Name : Full_Name; City : string[15]; State : string[2]; Zipcode : string[5]; Birthday : Date; end; var Friend Self,Mother,Father Temp Index : : : : array[1..Number_Of_Friends] of Person_Id; Person_Id; Person; byte;
begin (* main program *) New(Self); (* create the dynamic variable *) Self^.Name.First_Name := 'Charley'; Self^.Name.Initial := 'Z'; Self^.Name.Last_Name := 'Brown'; with Self^ do begin City := 'Anywhere'; State := 'CA'; Zipcode := '97342'; Birthday.Day := 17; with Birthday do begin Month := 7; Year := 1938; end; end; (* all data for self now defined *) New(Mother); Mother := Self; New(Father); Father^ := Mother^; for Index := 1 to Number_Of_Friends do begin New(Friend[Index]); Friend[Index]^ := Mother^; end; Temp := Friend[27]^; Write(Temp.Name.First_Name,' '); Temp := Friend[33]^; Write(Temp.Name.Initial,' '); Temp := Father^; Write(Temp.Name.Last_Name); Writeln; Dispose(Self); Dispose(Mother); } (* since Mother is lost, it cannot be disposed of *) Dispose(Father); for Index := 1 to Number_Of_Friends do Dispose(Friend[Index]); end. (* of main program *) {
A couple of pages ago, we discussed the fact that we had to break the great rule of Pascal and use an identifier before it was defined. This is the reason the exception to the rule was allowed. Since the pointer points to the record, and the record contains a reference to the pointer, one has to be defined after being used, and by rules of Pascal, the pointer can be defined first, provided that the record is defined immediately following it. That is a mouthful but if you just use the syntax shown in the example, you will not get into trouble with it.
(* Chapter 12 - Program 5 *) program Linked_List_Example; type Next_Pointer = ^Full_Name; Full_Name = record First_Name : string[12]; Initial : char; Last_Name : string[15]; Next : Next_Pointer; end; var Start_Of_List Place_In_List Temp_Place Index : : : : Next_Pointer; Next_Pointer; Next_Pointer; integer;
begin
(* main program *) (* generate the first name in the list *) New(Place_In_List); Start_Of_List := Place_In_List; Place_In_List^.First_Name := 'John'; Place_In_List^.Initial := 'Q'; Place_In_List^.Last_Name := 'Doe'; Place_In_List^.Next := nil; (* generate another name in the list *) Temp_Place := Place_In_List; New(Place_In_List); Temp_Place^.Next := Place_In_List; Place_In_List^.First_Name := 'Mary'; Place_In_List^.Initial := 'R'; Place_In_List^.Last_Name := 'Johnson'; Place_In_List^.Next := nil; (* add 10 more names to complete the list *) for Index := 1 to 10 do begin Temp_Place := Place_In_List; New(Place_In_List); Temp_Place^.Next := Place_In_List; Place_In_List^.First_Name := 'William'; Place_In_List^.Initial := 'S'; Place_In_List^.Last_Name := 'Jones'; Place_In_List^.Next := nil; end; (* display the list on the video monitor *) Place_In_List := Start_Of_List; repeat Write(Place_In_List^.First_Name); Write(' ',Place_In_List^.Initial); Writeln(' ',Place_In_List^.Last_Name); Temp_Place := Place_In_List; Place_In_List := Place_In_List^.Next; until Temp_Place^.Next = nil; end. (* of main program *)
{ Result of execution John Q Doe Mary R Johnson William S Jones William S Jones William S Jones William S Jones William S Jones William S Jones William S Jones William S Jones William S Jones William S Jones }
STILL NO VARIABLES?
It may seem strange, but we still have no variables defined, except for our old friend Index. In fact, for this example, we will only define 3 pointers. In the last example we defined 54 pointers, and had lots of storage room. Before we are finished, we will have at least a dozen pointers but they will be stored in our records, so they too will be dynamically allocated. Let's look at the program itself now. In line 20, we create a dynamically allocated record and define it by the pointer named Place_In_List. It is composed of the three data fields, and another pointer. We define Start_Of_List to point to the first record created, and we will leave it unchanged throughout the program. The pointer Start_Of_List will always point to the first record in the linked list which we are building up. The data space is as depicted in figure 12-7.
PROGRAMMING EXERCISE
1. Write a program to store a few names dynamically, then display the stored names on the monitor. As your first exercise in dynamic allocation, keep it very simple.
PART OF A PROGRAM
Load the program named AREAS.PAS and display it on your monitor. This is the first example of a TURBO Pascal unit and although it is similar to a program in many ways, it has a few differences which must be pointed out. We will start by pointing out the major sections, then get into the details of each section. You will first notice that this program begins with the reserved word unit instead of our usual program, followed by the unit name, Areas. In line 10, the reserved word interface is used and all of the statements following it down to the next reserved word implementation, are part of the interface with any program outside of this unit. The reserved word, implementation, defines the beginning of the definitions and executable parts of the private portion of the unit. Finally, in lines 48 through 50, we find what appears to be a program block just like we have been using all through this tutorial, but actually is not. We will see in a few paragraphs that this is the initialisation section and does a very specific job for us even though somewhat different than what we have become used to.
(* Chapter 13 - Program 1 *) unit Areas; (*****************************************************************) (* *) (* This unit includes a collection of functions to calculate the *) (* areas of four different geometric shapes. *) (* *) (*****************************************************************) interface function function function function
Area_Of_Circle(Radius : real ) : real; Area_Of_Square(Length_Of_Side : real) : real; Area_Of_Rectangle(Length,Width : real) : real; Area_Of_Triangle(Base, Height : real) : real;
implementation var My_Pi : real; procedure Mult_Two_Numbers(Number1, Number2 : real; var Result : real); begin Result := Number1 * Number2; end; function Area_Of_Circle; var Rad_Squared : real; begin Mult_Two_Numbers(Radius,Radius,Rad_Squared); Area_Of_Circle := Rad_Squared * My_Pi; end; function Area_Of_Square; begin Area_Of_Square := Length_Of_Side * Length_Of_Side; end; function Area_Of_Rectangle; begin Area_Of_Rectangle := Length * Width; end; function Area_Of_Triangle; begin Area_Of_Triangle := Base * Height / 2.0; end; begin (* This is the initialization code *) My_Pi := 3.14159267; end. (* of unit Areas *)
These four functions are available for use in any program in much the same way that any of the standard Pascal functions are available for use. The only difference is that a uses clause must be included in order to use these functions.
A LOCAL PROCEDURE
In lines 20 through 24, we have a procedure that is used within one of the four functions, namely the first. It is really a stupid procedure since it really wastes time setting up linkage for the procedure call and does nothing that couldn't be done just as easy with a simple multiply, but it does illustrate that you can use another procedure within the unit body. The procedure Mult_Two_Numbers cannot be used outside of this unit because it is not included in the interface part of the unit. It is, in effect, invisible to the outside world. The variable My_Pi would be more correctly represented as a constant but it is defined as a variable to illustrate the use of the body of the unit later. Since My_Pi is not defined in the interface part of the unit, it also is invisible to the outside world and in fact protected from accidental corruption by a misplaced statement in another program. The procedure Mult_Two_Numbers and the variable My_Pi for all practical purposes have an impenetrable barrier around them protecting them from unauthorised use or modification by the outside world, but the functions internal to this unit have free access to them just as in any other program.
If you will study the interface part of this unit you will find that everything you need to use this unit is contained within it, provided that you know enough about plane geometry to understand the functions. You should strive for this understanding in all of your interfaces so that the implementation doesn't even require consultation. Keep in mind, that if you need to, you can include comments to further define the functions in the interface part of the unit. At this time, you should compile this unit. You will have to compile it to disk rather than only to memory so it will be available for use later in this chapter. You do this by using the menus to change the Compile/Destination to the Disk option. Note that it will not generate an .EXE file but instead a .TPU file. This is Borland's filename extension for a unit.
ANOTHER UNIT
Load the file named PERIMS.PAS for another example of a unit. This is similar to the last except that it does not contain an internal procedure, and it is composed of three procedures that calculate the perimeters of geometric shapes, all of which are visible to the outside world because they are included in the interface part of the unit. Once again, we have a private variable named My_Pi and a block of code (actually a single statement) to initialise the value of My_Pi when the unit is loaded.
(* Chapter 13 - Program 2 *) unit Perims; (*****************************************************************) (* *) (* This unit contains three procedures to calculate perimieters *) (* of three geometric shapes. *) (* *) (*****************************************************************) interface procedure Perimeter_Of_Circle(Radius : real; var Perimeter : real ); procedure Perimeter_Of_Square(Length_Of_Side : real; var Perimeter : real); procedure Perimeter_Of_Rectangle(Length,Width : real; var Perimeter : real); implementation var My_Pi : real;
begin (* This is the initialization code *) My_Pi := 3.14159267; end. (* of unit Perimeters *)
Be sure you compile this unit to disk in the same manner as the last and they will be ready for use. Note that it is not a requirement that a unit be composed of only functions or only procedures. They can be freely mixed in a unit. It was only done this way in this example because it was convenient. Now that we have several functions and procedures that can be used to calculate the areas or perimeters of several different shapes, we need a program to illustrate their use, so if you load and display the program named GARDEN.PAS you will have an example of their use.
{ Result of execution The radius of the garden is 12.0 feet. The area to plow is 452.4 square feet. A wall around the garden will be 75.4 feet long. }
(* Chapter 13 - Program 4 *) program Calculate_Area_Of_Shapes; uses Areas,Crt; var In_Char : char; Length,Width,Height,Base,Radius : real; begin (* main program *) repeat Writeln; Writeln('Please input the first letter of the selection'); Writeln('Select shape; Square Rectangle Triangle Circle Quit'); Write('Requested shape is '); Repeat until Keypressed; In_Char := ReadKey; case UpCase(In_Char) of 'S' : begin Write('Square Enter length of side '); Readln(Length); Writeln('The area is ',Area_Of_Square(Length):12:4); end; 'R' : begin Write('Rectangle Enter width '); Readln(Width); Write('Enter height '); Read(Height); Writeln(' The area is ', Area_Of_Rectangle(Width,Height):12:4); end; 'T' : begin Write('Triangle Enter base '); Readln(Base); Write('Enter height '); Read(Height); Writeln(' The area is ', Area_Of_Triangle(Base,Height):12:3); end; 'C' : begin Write('Circle Enter radius '); Readln(Radius); Writeln('The area is ',Area_Of_Circle(Radius):12:3); end; 'Q' : Writeln('Quit'); else Writeln(' undefined entry'); end; until (In_Char = 'Q') or (In_Char = 'q'); end. (* of main program *)
{ Result of execution (The output depends on the data entered at the keyboard) }
variable name Areas.My_Pi would refer to that variable from the unit named Areas, and the name Perims.My_Pi would refer to the variable from the unit named Perims. You could even define a new variable of the same name in your main program and refer to it by the qualified name Garden.My_Pi if you chose to. This is not recommended as it would get very confusing to you. The compiler would be very happy to compile and run such a program, because it would not get confused. It is not illustrated in the example program, but this technique applies to procedure and function names as well. If you used the same procedure name in two different units, you could specify which procedure you intend to use by using the dot notation with the unit name and the procedure name. Unit_Name.Procedure_Name would therefore refer to the procedure named Procedure_Name that is a part of the unit named Unit_Name.
PROGRAMMING EXERCISE
1. Move My_Pi to the interface in both units and change one of the values slightly to see if you can read in the right one at the right time. Define another variable of the same name in your main program and see if you can differentiate between all three values. Note that, due to the nature of this exercise, no answer is given for it on the distribution disk.
(* Chapter 14 - Program 1 *) program Encapsulation_1; type Box = object length : integer; width : integer; constructor Init(len, wid : integer); procedure Set_Data(len, wid : integer); function Get_Area : integer; end; constructor Box.Init(len, wid : integer); begin length := len; width := wid; end; procedure Box.Set_Data(len, wid : integer); begin length := len; width := wid; end; function Box.Get_Area : integer; begin Get_Area := length * width; end; var Small, Medium, Large : Box; begin Small.Init(8,8); Medium.Init(10,12); Large.Init(15,20); WriteLn('The area of the small box is ',Small.Get_Area); WriteLn('The area of the medium box is ',Medium.Get_Area); WriteLn('The area of the large box is ',Large.Get_Area); end.
{ Result of execution The area of the small box is 64 The area of the medium box is 120 The area of the large box is 300 }
WHAT IS A METHOD?
A method is a term used with object oriented programming, and for the time being we will simply say that a method is either a function or a procedure (including a constructor). A method is therefore a method for doing an operation on some data. Lines 8 through 10 are method headers and give the pattern for all calls to these methods which can be used by the compiler to check for the correct number and types of parameters. Once again, we promise to discuss the constructor soon. For the time being, simply think of it as another procedure. The entire object type definition is given in lines 5 through 11. This object contains two variables named length and width, each of type integer, and three methods which can be used to operate on the two variables. In the same manner that the definition of a type in Pascal does not actually give you a
variable to use, only a pattern, the definition of an object type does not give you an object. We will declare the objects when we get to line 30 of this program. You will note that we are already using new terminology, but this is necessary. The field of object oriented programming has its own vocabulary and in order for you to understand technical articles in this field, you must begin now to learn the new terminology. It won't be too long until you feel somewhat comfortable with it.
AN INSTANCE OF AN OBJECT
We need another new term at this point. When we use the object type to declare variables of that type as we do in line 30, we are creating instances of that object type. An instance is like a variable in conventional Pascal (non object oriented), except that it can do more and has some very interesting properties that a simple variable does not have. In line 30 we have created three instances of the object type named Box and each has two simple variables associated with it. Three methods are available which can be called to operate on these variables. We therefore have three objects named Small, Medium, and Large. In order to initialise the values stored within the objects we call the three objects in lines 34 through 36 to store values in their internal variables by dotting the name of the object to the name of the method we wish to call. You will note that this looks like the same technique we use to refer to the fields of a record. We display the area of the three boxes in lines 38 through 40 using the same technique used to initialise the values stored, and the program is complete. We seem to have accomplished very little with this program that we could not have more easily accomplished with an even shorter standard Pascal program, and that is true. This program is only meant to introduce some of the mechanics of object oriented programming. Additional programs will be used to illustrate some of the uses of this new technique.
NEW TERMINOLOGY
You may note that we switched terminology halfway through the above paragraphs. We began by referring to the object types as object types and calling the variables declared in line 30 instances. Later we began calling the instances objects. In this tutorial we will refer to the types as object types and the variables either as objects or instances. This terminology is consistent with current practice and should help you learn the new terminology.
Another very important point is the fact that we pass a message to a method rather than call a subprogram as in conventional Pascal. The difference is rather subtle, but there really is a difference as we will see a little later in this tutorial.
MORE ENCAPSULATION
The example program named ENCAP2.PAS uses most of the same techniques as the last program but this is much more meaningful since it illustrates one of the simplest advantages of using object oriented programming. In this program, we define two object types in lines 5 through 21, Box and Pole. Each has its own unique kinds of variables associated with it, and each has three methods that can be used with these kinds of data. The method definitions in lines 35 and 46 clearly illustrate why the object name must be associated with the method name in the method implementation. This allows you to use the same method name in more than one object definition. In addition to the two method definitions named Set_Data given here, we could also define and use another procedure with the name Set_Data that was a normal Pascal procedure just like any others we have used in prior chapters of this tutorial. Using the same method name in several places is often referred to as name overloading in object oriented programming terminology. You will note that in lines 5 through 21 we define the object types which define what each object will do. In lines 23 through 55 we define the method implementations which define how we do it. It is
assumed that you know enough Pascal at this point to understand what each method does, so nothing more will be said about the details of this program except for the private types.
(* Chapter 14 - Program 2 *) program Encapsulation_2; type Box = object constructor Init(len, wid : integer); procedure Set_Data(len, wid : integer); function Get_Area : integer; private (* Remove this if you are using TURBO Pascal 5.5, *) length : integer; (* and move both variables *) width : integer; (* ahead of the methods. *) end; Pole = object constructor Init(hei, dep : integer); procedure Set_Data(hei, dep : integer); function Get_Total_Length : integer; private (* Remove this if you are using TURBO Pascal 5.5, *) height : integer; (* and move both variables *) depth : integer; (* ahead of the methods. *) end; constructor Box.Init(len, wid : integer); begin length := len; width := wid; end; constructor Pole.Init(hei, dep : integer); begin height := hei; depth := dep; end; procedure Box.Set_Data(len, wid : integer); begin length := len; width := wid; end; function Box.Get_Area : integer; begin Get_Area := length * width; end; procedure Pole.Set_Data(hei, dep : integer); begin height := hei; depth := dep; end; function Pole.Get_Total_length : integer; begin Get_Total_length := height + depth; end; var Small, Medium, Large : Box; Short, Average, Tall : Pole; begin Small.Init(8,8); Medium.Init(10,12); Large.Init(15,20); Short.Init(8,2); Average.Init(12,3); Tall.Init(17,4); (* Small.length := 7; *) WriteLn('The area of the small box is ',Small.Get_Area); WriteLn('The area of the medium box is ',Medium.Get_Area); WriteLn('The area of the large box is ',Large.Get_Area);
WriteLn('The overall length of the short pole is ', Short.Get_Total_Length); WriteLn('The overall length of the average pole is ', Average.Get_Total_Length); WriteLn('The overall length of the tall pole is ', Tall.Get_Total_Length); WriteLn; Small.Set_Data(6,7); Short.Set_Data(6,1); Average.Set_Data(11,2); WriteLn('The area of the small box is ',Small.Get_Area); WriteLn('The area of the medium box is ',Medium.Get_Area); WriteLn('The area of the large box is ',Large.Get_Area); WriteLn('The overall length of the short pole is ', Short.Get_Total_Length); WriteLn('The overall length of the average pole is ', Average.Get_Total_Length); WriteLn('The overall length of the tall pole is ', Tall.Get_Total_Length); end.
{ Result of execution The The The The The The The The The The The The } area of area of area of overall overall overall area of area of area of overall overall overall the small box is 64 the medium box is 120 the large box is 300 length of the short pole is 10 length of the average pole is 15 length of the tall pole is 21 the small box is 42 the medium box is 120 the large box is 300 length of the short pole is 7 length of the average pole is 13 length of the tall pole is 21
really work at it to get that meaningless answer because the system itself would prevent us from accidentally using the wrong data. This is true only if we have agreed not to use any of the data directly but to do all data access through the available methods. Encapsulation is a form of information hiding, but TURBO Pascal has a rather weak form of it because, as mentioned earlier, Borland chose not to make the variables within the object private in version 5.5. In TURBO Pascal 6.0 the variables can be defined as private variables as mentioned earlier, and are therefore inaccessible outside of the unit in which they are declared. The client would be forced to use only the methods provided by the author of the object to access the contained data. This is true information hiding and adds some degree of protection to the internal data. If you are using TURBO Pascal 5.5, it is up to you to never refer to the data within the object directly as stated by Borland in the OOP Guide included with the compiler. Even if you are using version 6.0, you must still discipline yourself to not refer to the private data within the defining unit. The careful student will notice that since all data is carefully tied up within the objects, inadvertent mixing of the wrong data is impossible provided a few simple rules are followed as discussed above. Once again, this is such a small program that it is difficult to see the advantage of going to all of this trouble. In a larger program, once the objects are completed, it is a simple matter to use them knowing that they are debugged and working. After the data are all printed out, some of the variables are changed in lines 80 through 82, and the same output statements are used to reprint the same data so you can observe the changes.
WHAT IS A CONSTRUCTOR?
It is time to keep our promise and define just what a constructor is. In this present context, that of simple objects, the constructor does very little for us, but we will include one for nearly every object to illustrate its use. The constructor can be named anything desired but it would be best to stick with the convention and name every constructor Init as suggested by Borland. The constructor is used to initialise all values within an object and do any other setup that must be done to use an object. The constructor should be called once for every declared object. When we get to the topic of virtual functions, constructors will be absolutely required for every object, but for the simple objects we are using here, they are optional. It would be best to include a constructor in every object type, define the constructor to initialise all variables within the object, and call the constructor once for each instance of the object type. Until we get to virtual methods, none of this is required, but it would be good practice to get in the habit of doing it.
WHAT IS A DESTRUCTOR?
A destructor is another method that can be used for cleanup when you are finished with an object. It is usually used in conjunction with dynamic allocation to assure that all dynamically allocated fields associated with the object are deallocated prior to leaving the scope of the object. A destructor is not illustrated in this tutorial but it should be easy for you to define and use one when you have a need for one.
Car = object(Vehicle) Passenger_Load : integer; constructor Init(In_Wheels : integer; In_Weight : real; People : integer); function Passengers : integer; end;
Truck = Object(Vehicle) Passenger_Load : integer; Payload : real; constructor Init(People : integer; Max_Load : real; In_Wheels : integer; In_Weight : real); function Efficiency : real; function Wheel_Loading :real; end; { ********************* class implementations ******************** } constructor Vehicle.Init(In_Wheels : integer; In_Weight : real); begin Wheels := In_Wheels; Weight := In_Weight; end; function Vehicle.Get_Wheels : integer; begin Get_Wheels := Wheels; end; function Vehicle.Get_Weight : real;
begin Get_Weight := Weight; end; function Vehicle.Wheel_loading : real; begin Wheel_Loading := Weight/Wheels; end; constructor Car.Init(In_Wheels : integer; In_Weight : real; People : integer); begin Wheels := In_Wheels; Weight := In_Weight; Passenger_Load := People; end; function Car.Passengers : integer; begin Passengers := Passenger_Load; end; constructor Truck.Init(People : integer; Max_Load : real; In_Wheels : integer; In_Weight : real); begin Passenger_Load := People; Payload := Max_Load; Vehicle.Init(In_Wheels, In_Weight); end; function Truck.Efficiency : real; begin Efficiency := 100.0 * Payload / (Payload + Weight); end; function Truck.Wheel_Loading : real; begin Wheel_Loading := (Weight + Payload)/Wheels; end; { ************************ main program ************************** } var Unicycle : Vehicle; Sedan : Car; Semi : Truck; begin Unicycle.Init(1, 12.0); Sedan.Init(4, 2100.0, 5); Semi.Init(1, 25000.0, 18, 5000.0); WriteLn('The unicycle weighs ', Unicycle.Get_Weight:5:1, ' pounds, and has ', Unicycle.Get_Wheels, ' wheel.'); WriteLn('The car weighs ', Sedan.Get_Weight:7:1, ' pounds, and carries ', Sedan.Passengers, ' passengers.'); WriteLn('The semi has a wheel loading of ', Semi.Wheel_Loading:8:1, ' pounds per tire,'); WriteLn(' and has an efficiency of ', Semi.Efficiency:5:1, ' percent.'); with Semi do begin WriteLn('The semi has a wheel loading of ', Wheel_Loading:8:1, ' pounds per tire,'); WriteLn(' and has an efficiency of ', Efficiency:5:1, ' percent.'); end; end.
{ Result of execution The unicycle weighs 12.0 pounds, and has 1 wheel. The car weighs 2100.0 pounds, and carries 5 passengers. The semi has a wheel loading of 1666.7 pounds per tire, and has an efficiency of 83.3 percent. The semi has a wheel loading of 1666.7 pounds per tire, and has an efficiency of 83.3 percent. }
In lines 17 through 35, we declare two additional object types that use the Vehicle type as a base for the new types as indicated by the previously defined name Vehicle in parentheses in the object definitions in lines 17 and 26. The Vehicle object type is said to be the ancestor type and the two new object types are called descendant types. The descendant types inherit some information from the ancestor types according to well defined rules. The variables in the ancestor type are all included within the descendant types and are available in objects of the descendant types just as if they had been defined within the descendant types. For that reason, all variable names must be unique within the ancestor type and within each of the descendant types. A name can be reused in one or more descendants however, as is illustrated in lines 18 and 27 where the variable name Passenger_Load is used in both object types. The method names from the ancestor object types can be repeated in the descendant object types but this has the effect of overriding the method of the same name in the ancestor making the ancestor method unavailable for use in objects of the descendant types. Objects instantiated of the type Car therefore, have the three methods available in lines 11 through 13 of the ancestor type, the constructor in line 19 which overrides the constructor in line 10 of the ancestor type, and the function given in line 22. This object therefore has five different methods to perform its required operations. Objects of type Truck have five methods available also, the two in lines 11 and 12 and the one in line 33. The two in lines 29 and 34 of the descendant overrides the two in lines 10 and 13 of the ancestor object type. You should note that even though some of the methods were overridden in the descendant object type, they do not affect the ancestor, and instances of the Vehicle type have two variables and four methods available. In effect we have an object hierarchy which can be extended to as many levels as necessary to complete the task at hand. The most important part of object oriented programming is the definition of the objects in a meaningful manner, but it is not something you will learn to do overnight. It will take a great deal of practice until you can see the objects in any given project in such a way that a clear solution can be found. I was somewhat intimidated by the clever examples found in a classic text on object oriented programming until I talked to a man that had shared an office with the author at the time he was writing that particular book. I learned that what was finally put in the book was at least the fourth iteration of each problem and in some cases the seventh before he finally arrived at a good solution. We will have more to say about this topic as we progress through this tutorial.
In lines 96 through 98 we instantiate one of each and send a message to their constructors in lines 102 through 104 then print out a few of the stored values. Lines 113 through 116 are repeated in lines 120 through 123 where they are placed within a with section to illustrate that the with can be used for the calls to the methods in the same manner that it is used for accessing the fields of a record. Any other details of this program can be gleaned by the diligent student. Be sure to compile and execute this program so you can verify the given result.
AN OBJECT IN A UNIT
Load the example program named VEHICLES.PAS for an example of the proper way to package the object so it can be conveniently reused for another project. The object type definition is given in the public part of the unit so it is available to any Pascal program which needs to use it. The implementation of the methods are hidden in the implementation part of the unit where they are not directly available to any calling program. Note that it is also possible to define a few local methods within the implementation for use only within the implementation but none are illustrated here. There is no body to this unit, only the end statement in line 39 so there is no initialisation code to be executed during loading. It would be perfectly legal to include an initialisation body, but even if you do, you should be sure to include a constructor to be called once for each object. This is to prepare you for the use of virtual functions which we will study in the next chapter. Referring back to the discussion of the second example program in this chapter, you will find the means necessary to make the variables truly private in this program. If you move the two variables to a location just after the end of the methods, and add the reserved word private just before the two variables, they will be truly private and unavailable for direct modification outside of this unit. If you are using TURBO Pascal 6.0, you should make this modification and try to access the variables directly in the calling program which will be discussed next. You must compile this unit to disk so it can be used with the rest of the example programs in this chapter.
(* Chapter 14 - Program 4 *) unit Vehicles; interface type Vehicle = object Wheels : integer; Weight : real; constructor Init(In_Wheels : integer; In_Weight : real); function Get_Wheels : integer; function Get_Weight : real; function Wheel_loading : real; end; implementation constructor Vehicle.Init(In_Wheels : integer; In_Weight : real); begin Wheels := In_Wheels; Weight := In_Weight; end; function Vehicle.Get_Wheels : integer; begin Get_Wheels := Wheels; end; function Vehicle.Get_Weight : real; begin Get_Weight := Weight; end; function Vehicle.Wheel_loading : real; begin Wheel_Loading := Weight/Wheels; end; end.
(* Chapter 14 - Program 5 *) unit CarTruck; interface uses Vehicles; type Car = object(Vehicle) Passenger_Load : integer; constructor Init(In_Wheels : integer; In_Weight : real; People : integer); function Passengers : integer; end;
Truck = Object(Vehicle) Passenger_Load : integer; Payload : real; constructor Init(People : integer; Max_Load : real; In_Wheels : integer; In_Weight : real); function Efficiency : real; function Wheel_Loading :real; end; implementation constructor Car.Init(In_Wheels : integer; In_Weight : real; People : integer); begin Wheels := In_Wheels; Weight := In_Weight; Passenger_Load := People; end; function Car.Passengers : integer; begin Passengers := Passenger_Load; end; constructor Truck.Init(People : integer; Max_Load : real; In_Wheels : integer; In_Weight : real); begin Passenger_Load := People; Payload := Max_Load; Vehicle.Init(In_Wheels, In_Weight); end; function Truck.Efficiency : real; begin Efficiency := 100.0 * Payload / (Payload + Weight); end; function Truck.Wheel_Loading : real; begin Wheel_Loading := (Weight + Payload)/Wheels; end; end.
Note that this unit could have been further divided into two separate units, one for each object type, but it was felt that it was important to illustrate that several can be combined in this manner if desired. In like manner, the last unit could have been combined with this unit, but once again, it was desired to illustrate the generality of program decomposition and packaging.
{ Result of execution The unicycle weighs 12.0 pounds, and has 1 wheel. The car weighs 2100.0 pounds, and carries 5 passengers. The semi has a wheel loading of 1666.7 pounds per tire, and has an efficiency of 83.3 percent. The semi has a wheel loading of 1666.7 pounds per tire, and has an efficiency of 83.3 percent. }
{ Result of execution The unicycle weighs 12.0 pounds, and has 1 wheel. The car weighs 2100.0 pounds, and carries 6 passengers. The car weighs 2100.0 pounds, and carries 7 passengers. The car weighs 2100.0 pounds, and carries 8 passengers. The semi has a wheel loading of 1666.7 pounds per tire, and has an efficiency of 83.3 percent. The semi has a wheel loading of 1666.7 pounds per tire, and has an efficiency of 83.3 percent. }
This program is nearly identical to INHERIT2.PAS except for the addition of an array of Car type objects named Sedan[1] to Sedan[3], and the definition of a pointer to the Truck type object named Semi_Point. Lines 16 and 17 illustrate the initialisation of the array of Sedan, and lines 23 through 26 illustrates its use when the data is printed out. An object is dynamically allocated in line 18 and it is then initialised in the next line. Its use is illustrated in lines 28 through 40 and it is deallocated in line 41.
TURBO Pascal 5.5 and newer, have an extension to the New procedure allowing the dynamic allocation and the initialisation to take place in the same procedure call. The line; New(Semi_Point, Init(1, 25000.0, 18, 5000.0)); can be used to replace lines 18 and 19 in this program if you desire to do so. This program should illustrate that objects can be used with arrays and pointers in the same manner as a record. Be sure to compile and execute this program.
PROGRAMMING EXERCISES
1. Modify ENCAP2.PAS in such a way to multiply the height of the short pole times the length of the medium box and print the result out. Even though this is possible to do, it requires you to expend a bit of effort to accomplish. Remember that you should not use the components of an object directly, only through use of the available methods. Add an object named Pick_Up to INHERIT2.PAS of type Truck and initialize it to some reasonable values. Print out its loading and efficiency in a manner similar to the Semi.
2.
Car = object(Vehicle) Passenger_Load : integer; constructor Init(In_Wheels : integer; In_Weight : real; People : integer); procedure Message; end;
Truck = Object(Vehicle) Passenger_Load : integer; Payload : real; constructor Init(People : integer; Max_Load : real; In_Wheels : integer; In_Weight : real); procedure Message; end; { ********************* class implementations ******************** } constructor Vehicle.Init(In_Wheels : integer; In_Weight : real); begin Wheels := In_Wheels; Weight := In_Weight; end; procedure Vehicle.Message;
constructor Car.Init(In_Wheels : integer; In_Weight : real; People : integer); begin Wheels := In_Wheels; Weight := In_Weight; Passenger_Load := People; end; procedure Car.Message; begin WriteLn('This message is from the car.'); end;
constructor Truck.Init(People : integer; Max_Load : real; In_Wheels : integer; In_Weight : real); begin Passenger_Load := People; Payload := Max_Load; Wheels := In_Wheels; Weight := In_Weight; end; procedure Truck.Message; begin WriteLn('This message is from the truck.'); end;
procedure Output_A_Message(VAR Machine : Vehicle); begin Write('This is from Output_A_message; '); Machine.Message; end;
{ ************************ main program ************************** } var Unicycle : Vehicle; Sedan : Car; Semi : Truck; begin Unicycle.Init(1, 12.0); Sedan.Init(4, 2100.0, 5); Semi.Init(1, 25000.0, 18, 5000.0); WriteLn; Unicycle.Message; Sedan.Message; Semi.Message; WriteLn; Output_A_Message(Unicycle); Output_A_Message(Sedan); Output_A_Message(Semi); end.
{ Result of execution
This message is from the vehicle. This message is from the car. This message is from the truck. This is from Output_A_Message; This message is from the vehicle. This is from Output_A_Message; This message is from the vehicle. This is from Output_A_Message; This message is from the vehicle. }
Compile and execute the program and you will find that even though it is legal to pass the objects of type Car and Truck to the method named Output_A_Message in lines 109 and 110, the method that is called from line 86 is the method named Message in the parent type Vehicle. This is probably no surprise to you since we defined an object of type Vehicle as a formal parameter of the method Output_A_Message. We need only one small change and we will have a virtual procedure call. Even though this program seems to do very little, it will be the basis of our study of virtual methods so you should study the code in detail.
Car = object(Vehicle) Passenger_Load : integer; constructor Init(In_Wheels : integer; In_Weight : real; People : integer); procedure Message; virtual; end;
Truck = Object(Vehicle) Passenger_Load : integer; Payload : real; constructor Init(People : integer; Max_Load : real; In_Wheels : integer; In_Weight : real); procedure Message; virtual; end; { ********************* class implementations ******************** }
constructor Vehicle.Init(In_Wheels : integer; In_Weight : real); begin Wheels := In_Wheels; Weight := In_Weight; end; procedure Vehicle.Message; begin WriteLn('This message is from the vehicle.'); end;
constructor Car.Init(In_Wheels : integer; In_Weight : real; People : integer); begin Wheels := In_Wheels; Weight := In_Weight; Passenger_Load := People; end; procedure Car.Message; begin WriteLn('This message is from the car.'); end;
constructor Truck.Init(People : integer; Max_Load : real; In_Wheels : integer; In_Weight : real); begin Passenger_Load := People; Payload := Max_Load; Wheels := In_Wheels; Weight := In_Weight; end; procedure Truck.Message; begin WriteLn('This message is from the truck.'); end;
procedure Output_A_Message(VAR Machine : Vehicle); begin Write('This is from Output_A_message; '); Machine.Message; end;
{ ************************ main program ************************** } var Unicycle : Vehicle; Sedan : Car; Semi : Truck; begin Unicycle.Init(1, 12.0); Sedan.Init(4, 2100.0, 5); Semi.Init(1, 25000.0, 18, 5000.0); WriteLn; Unicycle.Message; Sedan.Message; Semi.Message; WriteLn; Output_A_Message(Unicycle); Output_A_Message(Sedan); Output_A_Message(Semi);
end.
{ Result of execution This message is from the vehicle. This message is from the car. This message is from the truck. This is from Output_A_Message; This message is from the vehicle. This is from Output_A_Message; This message is from the car. This is from Output_A_Message; This message is from the truck. }
Once again we send a message to Output_A_Message three times in lines 110 through 112 and line 88 is used to send a message to the Message method. When we compile and execute this program, we find that even though the method Output_A_Message only uses the parent type Vehicle, the system calls the correct procedure based on the type of the actual object passed to this method. The system sends a message to the objects of the correct type instead of to the parent type as may be expected. It should be clear to you that the object that is to receive the message is not known at compile time but must be selected at run time when the object arrives at the method Output_A_Message. This is known as late binding since the type is not known until run time as opposed to early binding where the type is known at compile time. Every subprogram call in this entire tutorial, up to this point, has been early binding. You will note that even though the method Output_A_Message only knows about the objects of type Vehicle, it has the ability to pass through other types, provided of course that they are descendant types of Vehicle. The method Output_A_Message only passes the message through, it does not do the selection. The selection is done by the objects themselves which answer the messages passed to them. This means that the sender does not know where the message will be answered from, and it is up to the receiver to find that a message is being sent its way and to respond to it. It is often said that the supplier (the method doing the work) must make the decision to answer the message, rather than the client (the user of the work done). The burden is placed on the supplier to do the right thing. If a method is declared virtual, all methods of that name must also be virtual including all ancestors and all descendants. It is not possible to declare part of the methods of the same name virtual and part standard. All parameter lists for all virtual methods of the same name must also be identical since they must all be capable of being called by the same method call.
location and tries to execute whatever happens to be there and could do almost anything at that unknown and undefined point in the code. So it is important to call a constructor once for each object as is done here so the pointer to the VMT can be initialised to the proper value. If you make several objects of one type, it is not enough to call a constructor for one object and copy that object into each of the other objects. Each object must have its own constructor call in order to prevent a system crash. The strange looking code in line 6 tells the system to check each call to a virtual function to see if the constructor has been called. This slows the program down slightly but will result in an error message if a virtual method is called prior to its VMT being properly set up with a constructor call. After a program is thoroughly tested, the code can be removed from line 6 to speed up the program slightly by eliminating the checks. Be warned however, that a call to a virtual method without A VMT will probably result in the computer hanging up.
Car = object(Vehicle) Passenger_Load : integer; constructor Init(In_Wheels : integer; In_Weight : real; People : integer); procedure Message; virtual; end; Car_Pointer = ^Car;
Truck = Object(Vehicle) Passenger_Load : integer; Payload : real; constructor Init(People : integer; Max_Load : real; In_Wheels : integer; In_Weight : real); procedure Message; virtual; end; Truck_Pointer = ^Truck;
{ ********************* class implementations ******************** } constructor Vehicle.Init(In_Wheels : integer; In_Weight : real); begin Wheels := In_Wheels; Weight := In_Weight; end; procedure Vehicle.Message; begin WriteLn('This message is from the vehicle.'); end;
constructor Car.Init(In_Wheels : integer; In_Weight : real; People : integer); begin Wheels := In_Wheels; Weight := In_Weight; Passenger_Load := People; end; procedure Car.Message; begin WriteLn('This message is from the car.'); end;
constructor Truck.Init(People : integer; Max_Load : real; In_Wheels : integer; In_Weight : real); begin Passenger_Load := People; Payload := Max_Load; Wheels := In_Wheels; Weight := In_Weight; end; procedure Truck.Message; begin WriteLn('This message is from the truck.'); end;
procedure Output_A_Message(VAR Machine : Vehicle_Pointer); begin Write('This is from Output_A_message; '); Machine^.Message; end;
{ ************************ main program ************************** } var Unicycle : Vehicle_Pointer; Sedan : Car_Pointer; Semi : Truck_Pointer; begin New(Unicycle); New(Sedan); New(Semi); Unicycle^.Init(1, 12.0); Sedan^.Init(4, 2100.0, 5); Semi^.Init(1, 25000.0, 18, 5000.0); WriteLn; Unicycle^.Message; Sedan^.Message; Semi^.Message; WriteLn; Output_A_Message(Unicycle); Dispose(Unicycle); Unicycle := Sedan; Output_A_Message(Unicycle); Unicycle := Semi; Output_A_Message(Unicycle); Dispose(Sedan); Dispose(Semi); end.
{ Result of execution This message is from the vehicle. This message is from the car. This message is from the truck. This is from Output_A_Message; This message is from the vehicle. This is from Output_A_Message; This message is from the car. This is from Output_A_Message; This message is from the truck. }
You will notice that once again, the methods named Message are all defined as virtual and a pointer type is defined for each object type. In lines 99 through 101, three pointers are declared and memory is dynamically allocated on the heap for the objects themselves. The objects are all sent a constructor message to initialise the stored data within the objects and to set up the VMT for each. The rest of the program is nearly identical to the last program except that Dispose procedures are called for each of the dynamically allocated objects. The code used in line 6 of the last program to force a check of each virtual method call has been removed to illustrate that it doesn't have to be there if you are sure a message is sent to a constructor once for each object with a virtual method. Compiling and executing this program will give the same result as the last program indicating that it is perfectly legal to use pointers to objects as well as the objects themselves.
AN ANCESTOR OBJECT
The example program PERSON.PAS is not a complete program at all but only an object definition within a unit. This unit should pose no problem for you to understand so we will not say much except to point out that the method named Display is a virtual method. This example program, as well as the next two example programs, have been carefully selected to illustrate the proper way to package objects for use in a clear understandable manner. Compile this unit to disk in order to make it available for use in the remainder of this chapter.
(* Chapter 15 - Program 4 *) unit Person; interface type Person_ID = object Name : string[30]; Salary : integer; constructor Init; procedure Display; virtual; end; implementation constructor Person_ID.Init; begin end; procedure Person_ID.Display; begin WriteLn('Error - this procedure should never be called.'); end; end.
: : : :
constructor Programmer.Init(In_Name : string; In_Salary : integer; In_Language : string); begin Name := In_Name; Salary := In_Salary; Language := In_Language;
end; procedure Programmer.Display; begin WriteLn(Name,' specializes in ',Language, ' and makes $', Salary, ' per month.'); end;
constructor Secretary.Init(In_Name In_Salary In_Shorthand In_Typing_Speed begin Name := In_Name; Salary := In_Salary; Shorthand := In_Shorthand; Typing_Speed := In_Typing_Speed; end;
: : : :
procedure Secretary.Display; begin WriteLn(Name,' can type ',Typing_Speed, ' words per minute.'); end; end.
(* Chapter 15 - Program 6 *) program Employee; {$R+} uses Person, Supervsr; var staff Sup Prog Sec Index begin for Index := 1 to 10 do staff[Index]^.Init; WriteLn('XYZ Staff assignments.'); WriteLn; new(Sup); staff[1] := Sup; Sup^.Init('Big John', 5100, 'President'); new(Prog); staff[2] := Prog; Prog^.Init('Joe Hacker', 3500, 'Pascal'); new(Prog); staff[3] := Prog; Prog^.Init('OOP Wizard', 7700, 'OOP Pascal'); new(Sec); staff[4] := Sec; Sec^.Init('Tillie Typer', 2200, True, 85); new(Sup); staff[5] := Sup; Sup^.Init('Tom Talker', 5430,'Sales Manager'); new(Prog); staff[6] := Prog; Prog^.Init('Dave Debug', 5725, 'Assembly Language'); for Index := 1 to 6 do staff[Index]^.Display; end. : : : : : array[1..10] of ^Person_ID; ^Supervisor; ^Programmer; ^Secretary; integer;
(* Result of execution XYZ Staff assignments. Big John is the president and makes $5100 per month. Joe Hacker specializes in Pascal and makes $3500 per month. OOP Wizard specializes in OOP Pascal and makes $7700 per month. Tillie TYper can type 85 words per minute. Tom Talker is the Sales Manager and makes $5430 per month. Dave Debug specializes in Assembly Language and makes $5725 per month. *)
PROGRAMMING EXERCISES
3. Add a new object type to SUPERVSR.PAS to define a Consultant defining appropriate data fields for him, then add a couple of Consultant type objects to EMPLOYEE.PAS to use the new object type.
Chapter 16 : COMPLETE SAMPLE PROGRAMS Prior to this point, this tutorial has given you many example programs illustrating a point of some kind, but these have all been "nonsense" programs as far as being useful. It would be a disservice to you to simply quit with only tiny programs to study, so the following programs are offered to you as examples of good Pascal programming practice. They are useful programs, but they are still short enough to easily grasp their meaning. We will discuss them one at a time.
1.
In the data input section, ask if a printout is desired, and only print if it was requested. This would involve defining a new variable and if statements controlling all write statements with Lst as a device selector. Format the printout with a form-feed every three years to cause a neater printout. The program presently prints data right across the paper folds with no regard to the top of page. Modify the program to include semi-monthly payments. Payments twice a month are becoming popular, but this program cannot handle them. Instead of listing the months as numbers, put in a case statement to cause the months to be printed out as three letter names. You could also include the day of the month when the payment is due. Any other modification you can think up. The more you modify this and other programs, the more experience and confidence you will gain.
2.
3.
4.
5.
LIST.PAS, to list your Pascal programs LIST.PAS is a very useful program that you can use to list your Pascal programs on the printer. It can only be compiled with TURBO Pascal because it uses a TURBO extension, the string type variable. The method used in the Initialize procedure to read the command line parameter should be no problem for you to understand at this point. To use this program to print out the last program, for example, you would enter the following at the DOS prompt LIST AMORT5.PAS. This program reads in the AMORT5.PAS from the command line and uses it to define the input file. It should be pointed out that this program cannot be run from a "compiled in memory" compilation with the TURBO Pascal compiler. It must be compiled to a Disk file, and you must quit TURBO Pascal in order to run it from the DOS command level. The parameter read from the command line, AMORT5.PAS, is stored at computer memory location 80(hexadecimal) referred to the present code segment. If you didn't understand that, don't worry, you can still find the input parameter in any program using the method given in the initialise procedure for your version of TURBO Pascal. If you are not using a TURBO Pascal compiler, but you are using MS-DOS or PC-DOS, you can still use this program because it is provided on your disk already compiled as LIST.EXE, and can be run like any other .COM or .EXE program.
TIMEDATE.PAS, to get today's time and date This is a very useful program as an example of using some of the extensions of TURBO Pascal. It interrogates the inner workings of DOS and gets the present time and date for you, provided you entered them correctly when you turned your computer on. The procedure Time_And_Date can be included in any TURBO Pascal program you write to give you the time and date for your listings. As an exercise in programming, add the time and date to the program LIST to improve on its usefulness. It turns out to be an almost trivial program but is still a good illustration of how to use some of the newer Borland extensions to Pascal. The observant student will notice that the time and date procedures have already been added to LIST.PAS.
SETTIME.PAS, a useful utility program This program is very interesting in that it changes the date and time stamp on any file in the current directory. It is the program used to set the time and date on all of the files on the distribution disk included with this tutorial. It sets the time to 12:00:00 and the date to Feb 4, 1991 but you can use it to set any desired time.
OT.PAS, The OAKTREE directory program This program should be very useful to you, especially if you have a hard disk. It will list the entire contents of your hard disk (or floppy) in a very easy to read and easy to use form. The program is documented in the file named OT.DOC. It uses many of the TURBO Pascal extensions and will probably not compile with any other Pascal compiler without extensive modifications. This is a very useful program, so you should spend the time necessary to both understand it and modify it for your own needs. You will find this program to be a good example of linked lists because it includes a sort routine using a dynamically allocated B-TREE and another sorting routine that uses a dynamically allocated linked list with a bubble sort. These methods are completely defined in Niklaus Wirth's book, "Algorithms + Data Structures = Programs", a highly recommended book if you are interested in advanced programming techniques. It might also be pointed out that OT.PAS makes use of recursive methods for both sorting and handling subdirectories. It is definitely an example of advanced programming methods, and it would be a good vehicle for your personal study.