25B. Equations and Algorithms on Lists


Defining functions by equations

Let's think about writing a definition of function length(L) that returns the length of list L. For example, length([2, 4, 6]) = 3 and length([ ]) = 0.

There is two key rules about lists that you should always keep in mind when defining functions for lists.

  1. There are two kinds of lists: empty lists and nonempty lists. When thinking about lists, think about each kind.

  2. Every nonempty list has a head and a tail. When thinking about a nonempty list, consider its head and tail.

It suffices to say how to compute the length of an empty list and how to compute the length of a nonempty list. But instead of thinking about an algorithm right now, let's focus on facts.

(length.1) length([]) = 0
(length.2) length(L)  = 1 + length(tail(L))  (when L is not empty)
Here are two important observations.

So we have two facts (equations) about the length function. But what we really want is an algorithm to compute the length of a list. Is it reasonable to say that those two equations define an algorithm? Let's try to compute length([2, 4, 6]) using them. The only thing we do is replace expressions with equal expressions, using facts (length.1) and (length.2) and a little arithmetic.

  length([2,4,6])
    = 1 + length([4,6])           by (length.2), since tail([2,4,6]) = [4,6]

    = 1 + (1 + length([6]))       by (length.2), since tail([4,6]) = [6]

    = 1 + (1 + (1 + length([])))  by (length.2), since tail([6]) = []

    = 1 + (1 + (1 + 0))           by (length.1)

    = 3                           by arithmetic


Converting to C++

Equations (length.1) and (length.2) are easy to convert into C++. Since there are two equations, there are two cases.

  int length(ConstList L)
  {
    if(isEmpty(L)) 
    {
       return 0;                   // by (length.1)
    }
    else 
    {
       return 1 + length(tail(L))  // by (length.2)
    }
  }
It is also okay to use C++ notation directly.
  int length(ConstList L)
  {
    if(L == NULL) 
    {
       return 0;
    }
    else 
    {
       return 1 + length(L->tail);
    }
  }
Use whichever form you prefer. But only use C++ notation in C++ function definitions, not in conceptual equations.


Example: Membership test

Let's use equations to define function member(x, L), which is true if x belongs to list L. For example, member(3, [2, 6, 3, 5]) = true and member(4, [2, 6, 3, 5]) = false.

Here are some equations. Operator 'or' is the logical or operator, the same as | | in C++.

(member.1) member(x, []) = false
(member.2) member(x, L)  = head(L) == x  or  member(x, tail(L))
The first equation should be clear. The empty list does not have any members. Let's try some examples of the second equation, (member.2). According to that equation,

member(6, 2:[4,6,8]) 
  = 6 == 2  or  member(6, [4,6,8])
  = false  or  true
  = true
(Remember the rule for hand-simulating recursive algorithms. Recursive calls are assumed to work correctly. You can do the same for checking equations.)


member(2, 2:[4,6,8]) 
  = 2 == 2  or  member(2, [4,6,8])
  = true    or  member(2, [4,6,8])
  = true
Notice that we do not need to evaluate member(2, [4, 6, 8]) because (true or x) is true regardless of what x is. This is a search problem, and the algorithm does not need to look at the entire list when the it finds what it is looking for.


member(5, 2:[4,6,8]) 
  = 5 == 2  or  member(5, [4,6,8])
  = false   or  false
  = false

Let's convert Equations (member.1) and (member.2) into a definition of member in C++.

  bool member(const int x, ConstList L)
  {
    if(L == NULL)
    {
      return false;
    }
    else
    {
      return x == L->head || member(x, L->tail);
    }
  }

Notice that, for both length and member, we wrote one equation for an empty list and one for a nonempty list. That is typical. In some cases, you need (or prefer) more equations, as the next example illustrates.


Exercises

  1. Using equations (length.1) and (length.2), show an evaluation of length([6,5,4,3]) by only replacing expressions by equal expressions. Answer