emacs.d/clones/lisp/colinallen.dnsalias.org/lp/node44.html

217 lines
7.9 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 Nested Lists and Expressions</TITLE>
</HEAD>
<BODY>
<meta name="description" value=" Recursion on Nested Lists and Expressions">
<meta name="keywords" value="lp">
<meta name="resource-type" value="document">
<meta name="distribution" value="global">
<P>
<BR> <HR>
<A HREF="node45.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="node43.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="node45.html"> Recursion on Numbers</A>
<B>Up:</B>
<A HREF="node41.html"> Programming Techniques</A>
<B> Previous:</B>
<A HREF="node43.html"> Recursion on Simple </A>
<BR> <HR> <P>
<H1> Recursion on Nested Lists and Expressions</H1>
<P>
So far we have worked only with simple lists. These are lists
for which the top-level elements are our only concern. For example,
given the list '(1 a (5 g) up), the top-level elements are: 1, a, (5
g), and up. But often we are interested in looking deeper than just
the top-level. A nested list consists of atoms and lists; the latter
may themselves be nested. Searching, deleting, inserting, replacing
atoms in a multi-level list, or evaluating arbitrarily complex
mathematical expressions are all naturally recursive problems on
nested lists. Such recursion is slightly more complicated than
recursion on simple lists, but it again follows a common, general
structure.
<P>
<BLOCKQUOTE>
<PRE><TT> <H4><b>RULE OF THUMB 3:
<P>
When recurring on a nested list, do three things:
<P>
1. check for the termination condition;
<P>
2. check if the first element of the list is an atom or a list;
<P>
3. recur with the ``first'' and the ``rest'' of the list.
<P>
</b></h4></TT></PRE>
</BLOCKQUOTE>
<P>
<BLOCKQUOTE>
<PRE><TT> <H4><b>Example 3:
<P>
Write a function, ``search,'' which takes a nested list and an atom, and
<P>
it returns <tt> 't</tt> if it finds the atom within the nested list and returns <tt> nil</tt>
<P>
otherwise.
<P>
</b></h4></TT></PRE>
</BLOCKQUOTE>
<P>
To write this function, let's take the steps recommended by Rule of
Thumb 3:
<DL COMPACT><DT>(1)
<DD> We will move through the list one element at a time and if we
reach the end without finding the given atom, we will return ().
To check for termination we can use the predicate ``null.''
<DT>(2)
<DD> At each step we will look at the first element of the list; if it
is an atom we will check if it equals the given atom. If they
match, we can return <code>'t</code> immediately, else we go on with the search
in the rest of the list. We can use the predicate ``atom'' to check
if the first element is an atom. We can use ``equal'' to test for
equality.
<DT>(3)
<DD> Lastly, if we discover that the first element is a list, we may
need to perform two searches: the first within the nested list
represented by the first element; the second within the ``rest'' of
the original list. If the result of either of these searches is
't, the overall result is also true. We can use the logical
function ``or'' to get this effect.
<P>
</DL>
The above translates into the following simple piece of code:
<BLOCKQUOTE>
<PRE> (defun search (lst elt)
(cond ((null lst) nil)
((atom (first lst))
(if (equal (first lst) elt)
't
(search (rest lst) elt)))
(t (or (search (first lst) elt)
(search (rest lst) elt)))))
</PRE>
</BLOCKQUOTE>
The following notation gives an idea of the execution of ``search'':
<BLOCKQUOTE>
<PRE>(search '(a (1 c) 2 7) 'c)
= (search '((1 c) 2 7) 'c)
= (or (search '(1 c) 'c) (search '(2 7) 'c))
= (or (search '(c) 'c) (search '(2 7) 'c))
= (or 't (search '(2 7) 'c))
= 't
</PRE>
</BLOCKQUOTE>
Note that ``or'' only needs to evaluate upto the first non-nil argument
to return true. Similarly, ``and'' only needs to evaluate upto the first
nil argument to return nil. Another interesting application of
recursion is the evaluation of mathematical or logical expressions.
<P>
<BLOCKQUOTE>
<PRE><TT> <H4><b>RULE OF THUMB 4:
<P>
When evaluating an expression, do three things:
<P>
1. check for the termination condition;
<P>
2. identify operator;
<P>
3. apply operator to recursive calls on the operands.
<P>
</TT></PRE>
</BLOCKQUOTE>
<P>
</b></h4><BLOCKQUOTE>
<PRE><TT> <H4><b>Example 4:
<P>
Write a function, ``evaluate,'' which takes a prefix expression represented
<P>
as a nested list and returns the numerical value represented by the
<P>
expression. Only +, -, and * may be used as operators and each operator
<P>
can have only two operands. Thus, <tt> (evaluate '(* (+ 4 6) 4))</tt> should
<P>
return 40.
<P>
</b></h4></TT></PRE>
</BLOCKQUOTE>
Let us again try to identify the three elements of the previous Rule
of Thumb:
<DL COMPACT><DT>(1)
<DD> Note that we are no longer working with a list of elements. The
argument of ``evaluate'' represents an expression. If this argument
is a list we know that it will have three parts: an operator, the
first operand sub-expression, the second operand sub-expression.
In this case, we will need to further evaluate the operands. If
the argument is a number, we can stop. We can use the predicate
``numberp'' to test for a numerical value.
<DT>(2)
<DD> We can identify the first element of the argument list as the
operator. For each operator we will need to apply a different
function.
<DT>(3)
<DD> For each possible operator we will recursively call evaluate on
the first and second operands.
<P>
</DL>
The above translates into the following simple piece of code:
<BLOCKQUOTE>
<PRE> (defun evaluate (expr)
(cond ((numberp expr) expr)
((equal (first expr) '+)
(+ (evaluate (second expr))
(evaluate (third expr))))
((equal (first expr) '-)
(- (evaluate (second expr))
(evaluate (third expr))))
(t
(* (evaluate (second expr))
(evaluate (third expr))))))
</PRE>
</BLOCKQUOTE>
Since there are only three possible operators, we can use the default
case for *. The following notation gives an idea of the execution
of ``evaluate'':
<BLOCKQUOTE>
<PRE>(evaluate '(* (+ 6 3) (- (+ -1 2) 3)))
= (* (evaluate '(+ 6 3)) (evaluate '(- (+ -1 2) 3)))
= (* (+ (evaluate 6) (evaluate 3))
(evaluate '(- (+ -1 2) 3)))
= (* (+ 6 (evaluate 3)) (evaluate '(- (+ -1 2) 3)))
= (* (+ 6 3) (evaluate '(- (+ -1 2) 3)))
= (* 9 (evaluate '(- (+ -1 2) 3)))
= (* 9 (- (evaluate '(+ -1 2)) (evaluate 3)))
= (* 9 (- (+ (evaluate -1) (evaluate 2))
(evaluate 3)))
= (* 9 (- (+ -1 (evaluate 2)) (evaluate 3)))
= (* 9 (- (+ -1 2) (evaluate 3)))
= (* 9 (- 1 (evaluate 3)))
= (* 9 (- 1 3))
= (* 9 -2)
= -18
</PRE>
</BLOCKQUOTE>
</H4></b></b></b></b></b></H4></b></H4></b></b></b></b></b></H4></b><BR> <HR>
<A HREF="node45.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="node43.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="node45.html"> Recursion on Numbers</A>
<B>Up:</B>
<A HREF="node41.html"> Programming Techniques</A>
<B> Previous:</B>
<A HREF="node43.html"> Recursion on Simple </A>
<BR> <HR> <P>
<BR> <HR>
<P>
<ADDRESS>
<I>&#169; Colin Allen &amp; Maneesh Dhagat <BR>
October 2006</I>
</ADDRESS>
</BODY>