Beginners guide to Pascal
One of the strangest things is that most common search term for this blog is Turbo Pascal, that I've mentioned once (now twice) in my personal history of computing. I will attempt to honor that by posting a small beginners guide to Pascal. All code presented here should be available for download at this link.
The toolset, compiler and editor
I will not be using the old Turbo Pascal 6.0 environment for these examples. It is not that accessible now as it was 15 years ago when I started to learn programming. Instead I recommend any text editor with syntax highlighting for pascal or delphi and Free Pascal compiler. This should be available for most operating system environments.
Your first program
I don't know why you would like to learn Pascal. It is pretty much a dead language and only lives on in Delphi, which is also on the decline. I can only assume that you need to learn Pascal as a programming assignment at school. If you like the syntax of Pascal and would like to find a similiar language more up to date I would suggest you'd look into Ada or Delphi. Both languages are much more up to date with todays standard and suitable for production use.
- Name of the program, remember the semi-colon at the end (;)
- Main program listing begins here
- Write Hello World! to console output
- End the program listing, the main end has a dot (.) instead of a semicolon (;)
When I compile this I get one .o object file and one executable. If I run the executable I will get "Hello World!" written to the console window. Cool!
fpc src/helloworld.pas -obin/helloworld
This will compile the source code to bin/helloworld (use helloworld.exe if you're in DOS32 environment).
A variable is an identifier that has a value. You use them to store values into memory for later processing. There are 6 main types in the Pascal programming language.
|integer||Numbers: 3, 34, 0, -345|
|real||Numbers with decimals: 3.0, -0.05, 3.14|
|char||Single characters: a, F, 2, !|
|string||Multiple characters: Hello World!, Cheers!, My dog is Sam|
|boolean||True or false: true, false|
|pointer||more about this later|
First you declare a variable identifier and type, then you may assign a value to that variable, and them you're ready to use the variable.
Write('With the width ' +); Write(width); Write(' and the height '); Write(height); Write(' you have the area '); Writeln(area); end.
And you will get the expected output.
With the width 12 and the height 4 you have the area 48
If you have a value that never changes, you should use a constant instead of a variable. Here's an example with the constant of PI.
Write('With the radius '); Write(radius:1:0); Write(' we have the area '); Writeln(area:6:2); end.
- The strange notation is formatting. It means that we should use 1 space for the number and display 0 decimals.
- The same way this means, use 6 spaces for the number with 2 decimals.
The expected output is
With the radius 5 we have the area 78.50
Read input from user
You easily read input from user with Readln.
Write('What is your shoe size: '); Readln(size);
Write('Hello '); Write(name); Write(', your shoesize is '); Writeln(size:3:1); end.
And the output looks like this. Yellow markings are input done by the user.
What is your name: Mikael What is your shoe size: 41.5 Hello Mikael, your shoesize is 41.5
Time to take a look at if-then-else and looping constructs in Pascal.
(* Initialize variables *) number := Random(Max) + 1; guess := -1; guesses := 0;
while guess <> number do begin Write('Guess the number: '); Readln(guess);
if guess > number then Writeln('Your guess was too high') else Writeln('Your gess was too low');
guesses := guesses + 1; end;
Write('You found the number in '); Write(guesses); Writeln(' tries.'); end.
- Rand(Max) will randomize a number of 0-999. We add 1 to get a random number of 1-1000.
- The while loop will not exit until guess is equal to number. Begin marks the beginning of a code block.
- Runs the next expression if guess is larger than number, otherwise runs the else case.
- Notice that semicolon (;) is missing when there is an else case.
The expected output is
The number is between 1-1000. Guess the number: 500 Your gess was too low Guess the number: 800 Your guess was too high Guess the number: 750 Your guess was too high Guess the number: 650 Your guess was too high Guess the number: 550 Your guess was too high Guess the number: 525 Your gess was too low Guess the number: 535 Your gess was too low Guess the number: 545 Your gess was too low Guess the number: 548 Your gess was too low Guess the number: 549 Your gess was too low You found the number in 10 tries.
As you notice, the code will tell us that "Your guess was too low" even when we're spot on. How do we fix that bug?
The for loop starts at a number and counts it up until it reaches a max.
for i := 1 to height do begin
(* Write empty spaces before building blocks *) for j := 1 to (height - i) do Write(' ');
(* Write building blocks ) for j := 1 to (i + (i - 1)) do Write('');
Writeln(); end; end.
- Will loop "height" number of times. First time i will be 1, second time it will be 2 and so on. Notice the begin that marks beginning of a block that ends on line 19.
- It's ok to have inner for loops and to use arithmetic expressions to calculate upper bound.
The output of this should be
Height of pyramid: 5 * *** ***** ******* *********
And we could also count down, with the keyword downto.
result := 1; for i := n downto 1 do result := result * i;
Write(n); Write('! = '); Writeln(result); end.
The output of this would be
Calculate factor of: 5 5! = 120
Case-else can be quite useful for building menu options.
while input <> 5 do begin Writeln('Welcome, please select any of the following'); Writeln('1. Vegetables'); Writeln('2. Fruit'); Writeln('3. Gardening tools'); Writeln('4. Electronic equipment'); Writeln('or 5 to quit'); Write('> '); Readln(input);
case input of
1..2 : Writeln('You selected food');
3, 4 : Writeln('You selected a tool');
5 : Writeln('Thank you, and welcome back')
Writeln('Error: Unrecognized option');
- You can scoop up cases based on ranges of values.
- or you can use discrete values
- Notice that this line has no semicolon ending, because there is an else on the next line.
- The case statement has an end; even without begin.
Part of the output looks like this
Welcome, please select any of the following 1. Vegetables 2. Fruit 3. Gardening tools 4. Electronic equipment or 5 to quit > 88 Error: Unrecognized option
String and arrays
Strings are arrays of characters. You can specify the length of the string when you define them, or leave the length out and have it set to 255. Who needs longer strings than that anyway?
begin Write('Enter the encryption key: '); Readln(enckey);
Write('Enter the phrase to encrypt (capital letters): '); Readln(data);
result := ''; for i := 1 to Length(data) do begin (* Add the encryption key digit to all characters *) encChar := Chr(((Ord(data[i]) + encKey) mod Alphabet) + Ord('A')); result := result + encChar; end;
Write('Encrypted phrase: '); Writeln(result); end.
- The Length(data) function will return the length of the array, or string in this case.
- Ord(c) will get ascii number for the character, mod is the modulus operator and Chr(i) will get the char for that ascii number. The result is an encrypted character.
I assume that you did recognize the ceasar cipher used. Not very strong, but works well on the feeble minded.
Enter the encryption key: 1337 Enter the phrase to encrypt (capital letters): SECRET Encrypted phrase: UGETGV
You can create a new array by saying var [name] : array[x..y] of [type]. Here's an example of the fibonacci calculation.
begin numbers := 1; numbers := 2;
(* Calculate fibonacci sequence *) for i := 3 to Max do numbers[i] := numbers[i - 2] + numbers[i - 1];
(* Print out the sequence *) Write('Fibonacci: '); for i := 1 to Max do begin Write(numbers[i]); Write(' '); end;
- The Max constant used for top limit of the array could as easily been a number. I used a constant here for reuse in both for statements.
And the expected printout as follows.
Fibonacci: 1 2 3 5 8 13 21 34 55 89
A procedure is some piece of code that you might want to cut out and call several times.
procedure Title; begin Writeln('*** PASCAL WILL RULE THE WORLD ***'); end;
procedure Separator; var i : integer; begin for i := 0 to Width do Write('*'); Writeln; end;
(* Main program begins here *) begin Separator; Title; Separator; end.
- Notice that this constant is global, which means that it can be reached within a procedure, example at line 12.
- This variable i is local for the procedure, which means that it will be destroyed when the execution ends the procedure.
********************************** *** PASCAL WILL RULE THE WORLD *** **********************************
You can send arguments to a procedure as I do with the Swap procedure below. I'm using the numbers array as a global variable. Shame on me.
(* Randomize digits in the array *) procedure Randomize; var i : integer; begin for i := 1 to Max do numbers[i] := Random(100); end;
(* Swap two values in the array *) procedure Swap(x, y : integer); begin numbers[x] := numbers[x] xor numbers[y]; numbers[y] := numbers[x] xor numbers[y]; numbers[x] := numbers[x] xor numbers[y]; end;
procedure Sort; var i, j : integer; begin for i := 1 to Max - 1 do begin for j := i + 1 to Max do begin if numbers[i] > numbers[j] then Swap(i, j); end; end; end;
procedure Print; var i : integer; begin for i := 1 to Max do begin Write(numbers[i]); Write(' '); end; Writeln; end;
(* Main program starts here *) begin Randomize; Sort; Print; end.
- You will recognize the infamous XOR swap algorithm here. We define two arguments with the type integer.
- We call the Swap procedure with the arguments i, and j which are positions in the array that needs swapping.
The expected output
5 27 29 38 38 42 43 47 54 54 59 60 62 64 71 84 84 85 89 96
Functions and recursion
The difference with functions and procedures is that functions will return a value which makes it usable for recursion, i.e. calling itself. Following function uses recursion to do a binary search algorithm on a sorted list. Complexity should me O(n log n).
var guess : integer;
function CreateVector() : Vector; var v : Vector; begin v := 27; v := 29; v := 38; v := 42; v := 43; v := 47; v := 54; v := 59; v := 60; v := 62;
CreateVector := v; end;
function Exists(min, max, search : integer; v : Vector) : boolean; var middle : integer; begin
(* Found *) if (search = v[min]) or (search = v[max]) then Exists := true
(* Not found *) else if max - min < 2 then Exists := false
(* Keep looking *) else begin middle := min + Trunc((max - min) / 2);
if (search >= v[middle]) then Exists := Exists(middle, max, search, v) else Exists := Exists(min, middle - 1, search, v);
(* Main program starts here *) begin Write('Test if a number is in vector: '); Readln(guess);
Writeln(Exists(Min, Max, guess, CreateVector())); end.
- I create a type alias for the array. It will be easier reference to it in function calls.
- A function that should return a Vector
- You return a value by giving the function the value you want to return.
- min, max and search are arguments of integer, and v is of Vector. The return result is true/false that indicates if value is found.
- Recursive call to the same function that is running.
Test if a number is in vector: 38 TRUE
Records and pointers - linked lists
Records can be used to bundle primitives together and the most useful combination is with pointers. As you can use pointers from one record to another record and create linked lists. In the following piece of code I will use the sieve of Eratosthenes to produce the first 100 primes.
List = record current : integer; next : ListRef; end;
var result : ListRef;
(* Build a list of integers ranging from min to max *) function Range(min, max: integer) : ListRef; var list : ListRef; begin New(list);
if min = max then list^.current := max
else begin list^.current := min; list^.next := Range(min + 1, max); end;
Range := list; end;
(* Filter all the specific factors from the list *) function FilterFactors(numbers : ListRef; factor : integer) : ListRef; var next : ListRef; begin if numbers = nil then FilterFactors := numbers
else if numbers^.current mod factor = 0 then begin next := numbers^.next; numbers^.next := nil; Dispose(numbers);
FilterFactors := FilterFactors(next, factor);
else begin numbers^.next := FilterFactors(numbers^.next, factor); FilterFactors := numbers; end; end;
(* Remove all numbers that aren't primes *) function FilterPrimes(numbers : ListRef) : ListRef; begin if numbers <> nil then begin numbers^.next := FilterFactors(numbers^.next, numbers^.current); numbers^.next := FilterPrimes(numbers^.next); end;
FilterPrimes := numbers; end;
(* Print the list *) procedure Print(numbers : ListRef); begin if numbers <> nil then begin Write(numbers^.current); Write(' ');
end; Writeln; end;
(* Main program starts here *) begin result := FilterPrimes(Range(2, 100)); Print(result); end.
- Alias the List pointer type to ListRef
- Define a record of an integer and a pointer to next item in the linked list
- Create space on the heap for a new list item
- Clear the memory that the pointer points to, to be used by other programs
The output is as expected
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
I'm impressed that you read this far. It must mean that you have almost as perverted mind as I have (that spent a whole Sunday writing Pascal examples). I could continue and tell you about writing and reading files from disc. I could go into graphics programming or object oriented programming with Pascal. I won't. Sorry. But I have uploaded all of my examples so that you can run them yourself in your favorite compiler. Have fun!
Update: This guide now has a follow up on units and objects.