It's probably a good idea to spend a few moments thinking about stacks, first the more theoretical abstract data type and second the more practical version we really want to work with.
Suppose we want to evaluate
(1 + 5) - (2 + 3)
Saying how to do this out loud (and being very wordy about it!), we'd
probably say something like:
1
to 5
and stash the result someplace''2
to 3
and stash the result someplace''1
to 5
, and
2
to 3
than how to subtract the last two
results. So here's another description of how to do it, which is
wordier but doesn't end up with so many special cases.
1
on a piece of paper and put it on the
table''
5
on a piece of paper and put it on top
of the last piece of paper''
2
on a piece of paper and put it on top
of the last piece of paper''
3
on a piece of paper and put it on top
of the last piece of paper''
This is an example of using a stack. A stack is a natural
data structure to use in applications where you need to retrieve data
from the structure in the reverse of the order in which it is
inserted (like we just did). The classic example is in expression
evaluation like we did above (in fact
there have been many, many pocket calculators on the market that use
stack-based operations. HP is best known, but far from alone). The
stack has two fundamental operations: push
and
pop
. push
puts data on the stack, and
pop
gets it back off.
We also normally define the stack operations as including
arithmetic: add()
adds the top two elements of the
stack together, while subtract()
subtracts the top
element from the second-from-the top. Our arithmetic evaluation now
looks like this:
push(1) push(5) push(pop() + pop()) push(2) push(3) push(pop() + pop()) push(-pop() + pop()
There is an alternative notation for arithmetic, developed by Lukasiewicz. In his notation, instead of putting the operation between the operands like we're used to, we put it after the operands, like this.
1 5 + 2 3 + -
The 1 5 +
means what we're used to seeing as
1 + 5
. The 2 3 +
means 2 + 3
.
The advantage of this notation is that it isn't necessary to have any sort of defined order of evaluation, nor to use parentheses.
The "theoretical" stack has two operations (in addition to
initialization and tests for empty): push
and
pop
. In practice, we also need to be able to examine
and modify the contents of the stack. This means we'll need an
additional operation.
The top
operation lets us look at the top element of
the stack. This operation actually turns up occasionally in
"theoretical" descriptions of stacks. We actually want more than
this: we will want to be able to examine an element at a distance
of n from the top of the stack. As you can guess, we'll
use indexed addressing for this.