Stack
A stack is a data structure that keeps the data in Last In First Out
(LIFO) manner. It has two main operations defined: push, pop. The data
can be accessed only the top-of-stack item. It can be easily
implemented as an array and use a "stack pointer" to keep track of the
latest item. For example, a stack of integer
stack[MAX]
bottom = 0
MAX = 100
sp = bottom
push x is
sp++, stack[sp] = x
pop is
a = stack[sp], sp--, ret a
To make it safe from unexpected behaviour, we must check for
"out-of-range" access to the stack. We can do this using two
additional operators: empty?, full?
empty?
is
if sp <= bottom ret
true else ret false
full? is
if sp >= MAX ret true
else ret false
then we add this check to push, pop.
push
x is
if full? then error "stack
is overflow"
else sp++, stack[sp] = x
pop is
if empty? then error
"stack is underflow"
else a = stack[sp], sp--,
ret a
Running time
All operations (push, pop) are running in a constant time O(1).
Applications
Stack is widely used in computer language execution. The "trace
of execution" when one function called another function resembles the
stack. Let "program-counter (PC)" stores the point of return of a
function call. See the following example: let f calls g
which calls h.
trace of execution
f ------- call
g (pc1) --------- ...
|
|
| call h (pc2) |
g -------- ----- g ret
| |
| |
pc3 h ---- h ret
call ads is
push pc
goto ads
ret is
pc = pop
then the following picture shows the stack that keeps "return point"
when h is during the execution:
top
| |
| pc2 |
| pc1 |
-------
bot
So, the "return points " are kept in the stack appropriately.
Another example of the use of stack, it can be used to "evaluate" a
postfix expression. Let an expression be a simple arithmetic
operations such as the
follwing "2 + 3". It composed of literals and arithmetic
operators. "2 + 3" is an infix expression, because the operator
is in the middle of its arguments. A
"postfix" expression is an expression that an operator is at the end of
its arguments, for example "2 3 +".
An evaluation is the execution of the operator in an expression to
return a value. So, eval "2 + 3" = 5. For a postfix, eval "2 3 +"
= 5. We can give rule to
evaluate a postfix expression as follows: (using a stack to support the
execution of the rules)
Assume we read one token of an expression at a time:
1) if it is a literal push it.
2) if it is an operator apply it.
Applying an operator means, poping its correct number of arguments off
the stack and do the operation then push the result back to the
stack. Let's try these rules on the expression "2 3 + 4 +":
I will give the picture of the stack for each token read:
read
"2" | 2 |
-----
read
"3" | 3 |
| 2 |
-----
read
"+" | 5 | is push ( pop
+ pop )
-----
read
"4" | 4 |
| 5 |
-----
read
"+" | 9 |
-----
stop
Infix to postfix
Another example of the use of stack is an algorithm to transform an
infix expression into a postfix expression (so that we can apply "eval"
above to
"execute" it.)
Let start with a naive rule set that almost work:
Read a token at a time at the end of
input it reads "#".
Let use two stacks: D (for data), and Op (for operator).
1) if it is a literal push it to D.
2) if it is an operator push it to Op.
3) while it is not "#"
look at the operator at
top-of-stack of Op
we will know its arity.
for i=1 to arity
out pop
D
will out the appropriate number of
argument
out pop Op
For a simple expression it seems to work. Let try it on "2 + 3 #"
D Op
read "2" | 2 |
| |
----- -----
read "+" | 2 |
| + |
----- -----
read "3" | 3 |
| + |
| 2 | -----
-----
read "#" out "3", out "2", out "+" --> "3 2 +"
But when the expression is nested it fails. Try this "2 + 3 + 4 #".
when it read "#" the stack picture is:
| 4 | | + |
| 3 | | + |
| 2 | -----
-----
executing the rule 3) we will get the following output:
"4 3 + 2 ? ... "
at ? it tries to pop D for one more argument but D will underflow.
How to repair the rules so that it work properly?
Functional application
Another interesting use of stack is to hold the data for a "functional"
computer language. A functional language can be imagine as a
computer language that does not use any variable name. How can
you write a program without using variables?
A procedural language:
square(x)
= x * x
How can we manipulate data without naming them? The answer is
simple, use stack. We never "name" data in a stack, don't
we? Let define some operator that manipulate data in a
stack. We need only one in this example: dup. (duplicate)
dup
is copy top-of-stack and push it
With "dup" we can write square as,
square
is dup mul
The way to use "square" is to write it as a postfix expression: "2
square" = 4. The rule to apply a function is the apply its sequence of
operators in its body
one-by-one. In this case "dup" and then "mul". Here is the
picture of stack:
read
"2" | 2 |
-----
read "square"
apply
"dup" | 2 |
| 2 |
-----
apply
"mul" | 4 |
-----
stop
Using a computer language this way, it will be free of
"side-effect". An application of a function has a property called
"referencial transparency" that
means an expression in the language has the same meaning no matter
wherever it is written, for example "4 square 2 square square".
One can infer the meaning of this expression by inferring the meaning
of "square" alone without bothering about its position in the
expression. Another name of this kind of the language is
"concatenative" language. (look it up on the web!).
Homework
Make amend to my rule set so that the infix-to-postfix algorithm work
correctly. Hand-in not later than next Wednesday (10 Sept 2008).
5 Sept 2008