Lecture 3

To review the elements of Pascal that we learned in this lecture (enumerated types, procedures, functions, recursions, variable scope), read the following sections of our Pascal Made Simple textbook:

In this lecture we mentioned bases other than 10, and one of our homework exercises uses them as well. If you are not familiar with writing numbers in different bases, please read this page:

At the end of the lecture I reviewed some basic properties of logarithms. We will be using logarithms a lot in this course. If you need to review them, you can do so here:

We will also soon be using limits, especially limits at infinity. If you need to review your knowledge of limits, please read these pages:

Here are a few more notes on various topics.

array bounds

Remember that all dynamic arrays have indices starting with 0! If you call setLength(a, 100), you will end up with an array with indices from 0 to 99. A write to a[100] writes to unallocated memory, which could corrupt other program variables or cause a crash.

To guard against this, you can use the {$r+} directive, which enables range checking. This has two consequences:

I recommend that you put this directive at the top of every program that uses arrays.

random cards

This program prints five random cards, using an enumerated type to represent suits:

type
  suit = (club, diamond, heart, spade);

var
  names: array[11..14] of string = ('jack', 'queen', 'king', 'ace');
  
  rank: 2..14;
  s: suit;
  i: integer;
  
begin
  randomize;
  
  for i := 1 to 5 do
    begin
      rank := random(13) + 2;
      s := suit(random(4));
      
      case rank of
        2..10: write(rank);
        else write(names[rank]);
      end;
      
      writeln(' of ', s, 's');
    end;
end.

ordinal types

Certain types are considered to be ordinal types: these include boolean, all integer types, char, and enumerated types. Values of ordinal types can be converted to/from integers. They can be used

if c in ['A'..'Z'] then …

exit

The exit statement immediately exits the nearest enclosing procedure or function:

exit;

If there is no enclosing procedure or function, the program exits.

In a function, exit can take a parameter indicating a value to be returned from the function:

exit(4);

recursion

A function can call itself; this is called recursion. Here is the factorial function, written recursively:

function factorial(n: integer): integer;
begin
  if n = 0 then
    factorial := 1
  else
    factorial := n * factorial(n - 1);
end;

We will take a deeper look at solving problems using recursion a bit later on in this course.

working with digits

The last digit in the decimal number d is d mod 10. All digits except the last are d div 10. To retrieve digits in any other base b, use b instead of 10.

This program adds up all the decimal digits in a number:

var
  n: integer;
  digits: integer = 0;
  
begin
  readln(n);
  
  while n > 0 do
    begin
      digits := digits + (n mod 10);
      n := n div 10;
    end;
    
  writeln(digits);
end.

characters and digits

As we saw in the lecture 1, the ord function converts a character to its ASCII value, and the corresponding function chr converts an ASCII value to a character.

The ASCII code for ‘0’ is not zero, so if c is a character holding a digit, you cannot convert it to the corresponding numeric value by simply calling the ord function. Instead, you need to subtract the value of ord('0'):

d := ord(c) – ord('0');

Similarly, to convert a numeric value d to the corresponding digit character:

c := chr(ord('0') + d);

converting an integer to a string

This program converts a number to binary (base 2) by retrieving one digit at a time and prepending the digits to a string:

var
  n: integer;
  s: string = '';

begin
  readln(n);
  repeat
    s := chr(ord('0') + (n mod 2)) + s;
    n := n div 2;
  until n = 0;
  writeln(s);
end.

converting a string to an integer

The function below parses an integer n from a string s (just like the library function strToInt). It generates the integer using a technique known as Horner’s method, which works as follows. We use a variable n to store the value of the digits we have seen so far, initializing n to 0. We pass over the digits in the string from left to right, and each time we see a new digit, we multiply n by 10 (the decimal base) and add the value of the new digit.

For example, if the string s = “347”, the number n is generated as follows:

Here is the code:

function toInt(s: string): integer;
var
  n: integer = 0;
  i: integer;

begin
  for i := 1 to length(s) do
    n := n * 10 + ord(s[i]) - ord('0');
  toInt := n;
end;

searching in sorted arrays

In a sorted array of length n, all elements are in order:

a[0] <= a[1] <= a[2] <= … <= a[n]

Consider the problem of searching for an integer (called the “key”) in a sorted array a. Here is a naive sequential search:

function iterative_search(a: array of integer; key: integer): boolean;
  var
    i: integer;
  begin
    for i := low(a) to high(a) do
      if a[i] = key then
        exit(true);
    exit(false);
  end;

A binary search will perform much better:

function binary_search(a: array of integer; key: integer): boolean;
  var
    lo, hi, mid: integer;
  begin
    lo := low(a);
    hi := high(a);
    while lo <= hi do
      begin
        mid := (lo + hi) div 2;
        if a[mid] = key then
          exit(true);
        if a[mid] < key then
          lo := mid + 1
        else  // a[mid] > key
          hi := mid - 1;
      end;
    exit(false);
  end;

In a binary search, we use two variables lo and hi to keep track of the range of indices where the key might be found. To see how this function works, consider the following compound condition:

  1. For all j < lo, a[j] < key

  2. For all j > hi, a[j] > key

This condition is called a loop invariant. It is not written in code; it is a mathematical statement that helps us show that the program is correct. The invariant is true at the beginning, and remains true after each loop iteration:

Whenever lohi we keep looping, since we might have a[j] = key for some j in the range lojhi. Eventually the loop will terminate: in particular, if lo = hi then the loop will either set lo to hi + 1 or will set hi to lo – 1, and in either case then lo > hi and the loop exits. At that point we can return false, since for all j either j < lo or j > hi, so by the loop invariant we have a[j] <> key for all j.