• Schedule
• Readings
• Labs
• Homework
Revise the flatten procedure from the reading on deep
recursion so that
it uses a ``to-do'' stack of unprocessed subtrees, like the second
version of sum-of-number-tree in
today's reading. How will the order of the :push! messages affect
the result of the procedure?
Some authors add a sixth operation to the definition of the stack ADT:
size, which returns the number of elements in the stack.
Extend the Scheme implementation of make-stack in the reading so
that the stacks it constructs will accept the message :size and
perform this operation when it is received.
Documents on the World Wide Web usually contain special strings, called tags, that serve as instructions to the browser about what the document contains, how it is structured, and how the text should be displayed. In many cases, tags occur in pairs: The opening tag marks the beginning of a region of text that constitutes some natural unit within the document structure or should be displayed in some special way, and closing tag marks the end of that region.
An opening tag is a sequence of letters and digits enclosed between a
less-than character at the beginning and a greater-than character at the
end. For instance, "<html>" is the opening tag for a document that
contains hypertext markup, and "<title>" is the opening tag for the
title of such a document. The corresponding closing tags look almost the
same, but a closing tag has a slash character after the less-than
character: "</html>", "</title>".
Create a new stack and name it tags. Push onto this stack the
opening tag "<html>". Next, push "<head>", the tag that
begins the header of a hypertext document, and then "<title>". Now
pop the stack. The tag that appears is the one that must be matched first
by a closing tag in order for the tags to be correctly nested. Pop the
stack two more times and confirm that the stack is a ``last-in, first-out''
data structure.
Firefox and other browsers use a stack of tags like this one -- a stack
containing tags that must eventually be matched but have not been matched
yet -- to determine whether the HTML document to be displayed is correctly
constructed. Write a Scheme procedure correctly-nested? that takes
a list of HTML opening and closing tags and determines whether they are
correctly nested.
> (correctly-nested? '("<html>" "<head>" "<title>" "</title>" "</head>" "<body>" "<b>" "</b>" "</body>" "</html>"))
#t
> (correctly-nested? '("<html>" "<head>" "</html>" "</head>"))
#f
If you use stacks frequently, you will find that several other operations on them are useful enough to write up once and for all and keep in a library. Here are a few that one might consider:
display-stack, which displays the elements of a given stack in
an appropriate format.stack-equal?, which determines whether the contents of two
given stacks are the same (that is, whether they contain elements that are
equal?, arranged in the same order).copy-stack, which constructs a new stack containing the same
elements as a given stack, in the same order.
Now that we understand how objects work, we are confronted in these cases
with a design issue: Should we implement these as free-standing procedures
that stand outside the stacks that they operate on and send messages to
them, or should we extend the repertoire of messages that a stack can
understand to include :display, :equal?, :copy, etc.?
One difficulty with implementing them as free-standing procedures is that, in that case, we will have to pop each element off the stack in order to examine the ones after it. However, ordinarily, we would not want to destroy the stack in order to display it, test it for equality with another stack, etc. So each of these procedures would have to store the popped elements temporarily and push them all back on again to restore the stack to the state in which it arrived.
Such an approach is less efficient than adding more messages to the
repertoire of messages that stacks can handle, since the internal
implementation of a :display could simply traverse the underlying
list, without using :push! and pop! at all. On the other
hand, every message that is added to the interface makes it that much
harder to reason about stacks, to guarantee that stack invariants are
always satisfied, and to reimplement stack objects with a different
underlying data structure.
Try implementing one or more of the operations listed above, first as a standalone procedure, then as a new stack message. What information would be it be useful for a programmer to have in order to decide which implementation is better and how to resolve the design issue?
I am indebted to Professor Henry Walker for some of the exercises in this lab.