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.
There are two kinds of lists: empty lists and nonempty lists. When thinking about lists, think about each kind.
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.
These are equations just as much as the ones that you have studied in mathematics. For example, you learned that x(y + z) = xy + xz. That tells you that two expressions yield the same value for all values of the variables x, y and z. Similarly, equation length([ ]) = 0 tells you that two expressions, length([ ]) and 0, yield the same value.
As you can see, each equation can have a proviso indicating requirements for it to hold; the second equation only holds when L is not empty. The second equation tells you, for example, that length([2, 4, 6, 8]) = 1 + length([4, 6, 8]). Since 4 = 1 + 3, that is true.
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
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.
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]) = trueNotice 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.
Using equations (length.1) and (length.2), show an evaluation of length([6,5,4,3]) by only replacing expressions by equal expressions. Answer