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.
Netscape 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
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.
The yield of a pair structure is a list of its ``leaves'' -- the
values that are contained in the pair structure but are not themselves
pairs (and are not empty lists). For instance, the yield of the pair
structure (((1 . 2) . 3) . (4 . (5 . 6))) is the list (1 2 3 4
5 6).
Using a stack to keep track of unfinished parts of the problem, as in
today's reading, define a tail-recursive
procedure called yield that takes any pair structure and returns its
yield.
Define a procedure called copy-stack that takes a stack as argument
and returns a new stack with exactly the same contents. Note that
it is not possible to do this without popping the given stack repeatedly,
to recover each of its elements in turn. Make sure that your procedure
restores every element that is popped from the given stack by pushing it
back on again before it returns the new stack.
Define a predicate called stack-equal? that takes two stacks as
arguments and determines whether they contain the same elements (as tested
by equal?) in the same order. Again, it is not possible to do this
without popping the given stacks, so make sure that your procedure restores
the elements it pops before returning.
I am indebted to Professor Henry Walker for some of the exercises in this lab.