emacs.d/clones/colinallen.dnsalias.org/lp/node43.html

212 lines
7.4 KiB
HTML
Raw Normal View History

2022-08-02 12:34:59 +02:00
<!DOCTYPE HTML PUBLIC "-//W3O//DTD W3 HTML 2.0//EN">
<!Originally converted to HTML using LaTeX2HTML 95 (Thu Jan 19 1995) by Nikos Drakos (nikos@cbl.leeds.ac.uk), CBLU, University of Leeds >
<HEAD>
<TITLE> Recursion on Simple Lists</TITLE>
</HEAD>
<BODY>
<meta name="description" value=" Recursion on Simple Lists">
<meta name="keywords" value="lp">
<meta name="resource-type" value="document">
<meta name="distribution" value="global">
<P>
<BR> <HR>
<A HREF="node44.html"><IMG ALIGN=BOTTOM ALT="next" SRC="next_motif.gif"></A>
<A HREF="node41.html"><IMG ALIGN=BOTTOM ALT="up" SRC="up_motif.gif"></A>
<A HREF="node42.html"><IMG ALIGN=BOTTOM ALT="previous" SRC="previous_motif.gif"></A> <BR>
<A HREF="lp.html"><B>Contents</B></A>
<B> Next:</B>
<A HREF="node44.html"> Recursion on Nested </A>
<B>Up:</B>
<A HREF="node41.html"> Programming Techniques</A>
<B> Previous:</B>
<A HREF="node42.html"> A Word about </A>
<BR> <HR> <P>
<H1> Recursion on Simple Lists</H1>
<P>
Lists are one of the basic data structures in Lisp. Very often
programmers need to manipulate lists. Think of the broad possibility
of operations one may want to perform on lists: counting the number
of elements in a list; searching for an element in a list; removing a
particular element from a list; replacing an element in a list; these
represent only a small number of possibilities. All of these problems
are inherently recursive. Also, their solutions all follow the same
structure.
<P>
<BLOCKQUOTE>
<PRE><TT> <H4><b>RULE OF THUMB 1:
<P>
When recurring on a list, do three things:
<P>
1. check for the termination condition;
<P>
2. use the first element of the list;
<P>
3. recur with the ``rest'' of the list.
<P>
</b></h4></TT></PRE>
</BLOCKQUOTE>
<P>
<BLOCKQUOTE>
<PRE><TT> <H4><b>RULE OF THUMB 2:
<P>
If a function builds a list using ``cons,'' return () at the
<P>
terminating line.
<P>
</b></h4></TT></PRE>
</BLOCKQUOTE>
<P>
<BLOCKQUOTE>
<PRE><TT> <H4><b>Example 1:
<P>
Write a function, ``remove,'' which takes a list and an element, and
<P>
returns the original list with the first occurrence of the element
<P>
removed.
<P>
</b></h4></TT></PRE>
</BLOCKQUOTE>
To solve this problem we will have to look at each of the elements of
the given list one by one, checking if it is the element to be
removed. We know that we can stop searching if we
<DL COMPACT><DT>(1)
<DD> have reached the end of the list without finding the
element to be removed, or
<DT>(2)
<DD> have found the element to be removed (we can stop, since
we are only interested in its first occurrence).
<P>
</DL>
Let's try to apply Rule of Thumb 1.
<DL COMPACT><DT>(1)
<DD> From the rule we know that at each step we want to recur with the
``rest'' of the list. Thus, at each recursive call the list will
get shorter by one element. We must stop when the list becomes
(). We can use the predicate ``null'' to check for this condition.
<P>
<DT>(2)
<DD> The second part states that we should try to use the first element
of the list. Before each recursive call we want to check if the
first element of the list equals the given element. We can use
the predicate ``equal'' to test for equality.
<P>
<DT>(3)
<DD> We must remember that the function ``remove'' returns the original
list with the first occurrence of the given element removed. When
we recur with the ``rest'' of the list, it is important to preserve
the elements that do not match the given element. Thus, in the
third part, we should use ``cons'' to save these elements.
<P>
</DL>
Note that we are building a list using ``cons,'' specifically a list of
elements excluding the element to be removed. Using Rule of Thumb 2,
we know that in such a case we should return () at the terminating
line. Thus, if the test for ``null'' returns true, our function will
return ().
<P>
The following solution clearly shows the three parts of Rule of Thumb
1 and also illustrates the use of Rule of Thumb 2:
<BLOCKQUOTE>
<PRE> (defun remove (lst elt)
(cond ((null lst) nil)
((equal (first lst) elt) (rest lst))
(t (cons (first lst)
(remove (rest lst) elt)))))
</PRE>
</BLOCKQUOTE>
The following notation gives an idea of the execution of ``remove'':
<BLOCKQUOTE>
<PRE>(remove '(a 1 c 2 c 7) 'c)
= (cons 'a (remove '(1 c 2 c 7) 'c))
= (cons 'a (cons '1 (remove '(c 2 c 7) 'c)))
= (cons 'a (cons '1 '(2 c 7)))
= (cons 'a '(1 2 c 7))
= '(a 1 2 c 7)
(remove '(a (1 q) 2) 'q)
= (cons 'a (remove '((1 q) 2) 'q))
= (cons 'a (cons '(1 q) (remove '(2) 'q)))
= (cons 'a (cons '(1 q) (cons 2 (remove '() 'q))))
= (cons 'a (cons '(1 q) (cons 2 '())))
= (cons 'a (cons '(1 q) '(2)))
= (cons 'a '((1 q) 2))
= '(a (1 q) 2)
</PRE>
</BLOCKQUOTE>
Note, Rule of Thumb 1 provides a general framework within which to
think about recursion. In different examples, the importance and
length of the three components may differ. In the following example
the second part of the rule (using the first element of the list)
comes into play only implicitly.
<P>
<BLOCKQUOTE>
<PRE><TT> <H4><b>Example 2:
<P>
Write a function, ``length,'' which takes a list and returns a count of
<P>
all the top level elements in the list.
<P>
</b></h4></TT></PRE>
</BLOCKQUOTE>
<div style="border: 1px solid red; background: #ffffdd; margin: 10px; text-align: center"><b>NOTE:</b> This example illustrates that you are allowed to redefine the 'length' function that is built into Lisp. Programmer beware!!</div>
The solution is:
<BLOCKQUOTE>
<PRE> (defun length (lst)
(cond ((null lst) 0)
(t (+ 1 (length (rest lst))))))
</PRE>
</BLOCKQUOTE>
We can identify the three components mentioned in Rule of Thumb 1:
<DL COMPACT><DT>(1)
<DD> We still use ``null'' to test for termination, but, since now we
want to return a count of top level elements, when we reach the
end of the list we return 0 (the length of a null list).
<DT>(2)
<DD> We only use the first element implicitly; we account for its
presence by adding a one to the value returned by the recursive
call.
<DT>(3)
<DD> We do recur with the ``rest'' of the given list. Although we
do not explicitly use <code>(first lst)</code>, the first element of the list
is not forgotten; by adding a one to the result of the recursive
call, we keep a track of the top level elements.
<P>
</DL>
The following notation gives an idea of the execution of ``length'':
<BLOCKQUOTE>
<PRE>(length '(a (2 q) 64))
= (+ 1 (length '((2 q) 64)))
= (+ 1 (+ 1 (length '(64))))
= (+ 1 (+ 1 (+ 1 (length '()))))
= (+ 1 (+ 1 (+ 1 0)))
= (+ 1 (+ 1 1))
= (+ 1 2)
= 3
</PRE>
</BLOCKQUOTE>
</H4></b></b></b></b></b></H4></b></b></b></H4></b></H4></b><BR> <HR>
<A HREF="node44.html"><IMG ALIGN=BOTTOM ALT="next" SRC="next_motif.gif"></A>
<A HREF="node41.html"><IMG ALIGN=BOTTOM ALT="up" SRC="up_motif.gif"></A>
<A HREF="node42.html"><IMG ALIGN=BOTTOM ALT="previous" SRC="previous_motif.gif"></A> <BR>
<A HREF="lp.html"><B>Contents</B></A>
<B> Next:</B>
<A HREF="node44.html"> Recursion on Nested </A>
<B>Up:</B>
<A HREF="node41.html"> Programming Techniques</A>
<B> Previous:</B>
<A HREF="node42.html"> A Word about </A>
<BR> <HR> <P>
<BR> <HR>
<P>
<ADDRESS>
<I>&#169; Colin Allen &amp; Maneesh Dhagat <BR>
March 2007 </I>
</ADDRESS>
</BODY>