Stacks and Queues Lab
Objectives
The goal of this lab is to gain experience with stack and queue data structures and their applications.
Introduction
In this lab, you will use stacks and queues to solve problems. To facilitate development, we provide the following ADT implementations for you to use:
Download all of these files to a working directory and use them to solve the problems in this lab. Here is the documentation for these classes:
class ArrayStack | | Implements a Last-In, First-Out (LIFO) data structure. | | push(x) Adds element x at the top of the stack | pop() Removes and returns element at top of the stack | top() Returns (but doesn't remove) element at top of stack | is_empty() Returns True if the stack is empty | __len__() Returns the number of items in the stack | class ArrayQueue | | Implements a First-In, First-Out (FIFO) data structure. | | enqueue(x) Adds element x at the back of the queue | dequeue() Removes and returns element at front of the queue | first() Returns (but doesn't remove) element at front of queue | is_empty() Returns True if the queue is empty | __len__() Returns the number of items in the queue |
You should create a new Python file to contain the code you write in this
lab. I suggest calling it "stack_lab.py
" for now. Remember that you
will need to import the above classes in your new code module before you can use
them. Example:
from t_array import Array from stack import ArrayStack from queue import ArrayQueue
At some point, you may wish to disable the turtle graphics to avoid the slowdown during testing. You can use the following line to do this:
Array.visible = False
Finally, here are two Python string methods that you may find useful for this lab:
str.isalpha() Returns True if all characters are alphabetic and there is at least one character, False otherwise. str.isnumeric() Returns True if all characters in the string are numeric characters and there is at least one character, False otherwise
Part 1 - Palindromes
Use a stack and queue to implement a more powerful version of the
is_palindrome()
function from Midterm 1. As a reminder, this
function implements simple palindome verification. Here
is the signature and docstring for that function:
def is_palindrome(text): """ Return True if text is a palindrome, False otherwise. A palindrome is a string that is identical to itself when reversed. For example, "madam", "dad", and "abba" are palindromes. Note: the empty string is a palindrome, as is every string of length one. """
Pure iteration-based solutions to this problem require either A) 2n
accesses (to reverse the string then compare every letter) or B) non-sequential
access (directly comparing items from the beginning and end of the list).
However, observe that if you add a certain sequence of elements to both a stack
and a queue, you can then retrieve them from those data structures in opposite
orders very easily due to the nature of the two structures. Exploit this fact to
implement is_palindrome
with exactly n
sequential accesses to text
, where n =
len(text)
.
For this lab, you should make the function more flexible than the version
you wrote for the midterm. Your lab solution should ignore whitespace and
punctuation, and all comparisons should be case-insensitive. Include some tests in a
main()
function. Examples of valid palindromes:
""
"a"
"aa"
"aaa"
"aba"
"abba"
"Taco cat"
"Madam, I'm Adam"
"A man, a plan, a canal: Panama"
"Doc, note: I dissent. A fast never prevents a fatness. I diet on cod."
Part 2 - Postfix Evaluation
On Friday (9/26), we discussed postfix notation (sometimes called "Reverse Polish Notation" or "RPN") for explicitly specifying the operation order of a mathematical expression. Here is some examples of normal (infix) expressions followed by their postfix equivalents:
"1 + 2" == "1 2 +"
"3 - 1" == "3 1 -"
"2 * 3" == "2 3 *"
"3 + (4 * 5)" == "4 5 * 3 +"
"5 + ((1 + 2) * 4) - 3" == "5 1 2 + 4 * + 3 -"
We also discussed an algorithm for evaluating postfix expressions using a stack:
- for each token in expression
- if token is a number
- stack.push(token)
- if token is an operator
- op2 = stack.pop()
- op1 = stack.pop()
- stack.push(op1 <op> op2)
- if token is a number
- return stack.pop()
Implement this algorithm by writing a function with the following signature:
def eval_postfix(expr): """ Return the value of evaluating 'expr' as a postfix expression. All operands should be integer numbers, and all standard arithmetic operators (+, -, *, /) are supported. All operands and operators should be separated in 'expr' by a single space. If the the expression is invalid, the function should raise an EvalError. """
You may use the following definition for EvalError
:
class EvalError(Exception): pass
Include some tests in a main()
function. Examples:
"1 2 +" = 3
"15 8 -" = 7
"2 3 *" = 6
"4 5 * 3 +" = 23
"5 1 2 + 4 * + 3 -" = 14
Testing (Optional)
Write some unit tests for the functions you wrote in this lab using the
Python unittest
module, as we practiced in a previous
lab. Try injecting flaws into your implementations to make sure that your
tests catch the flaws.
Alternatively, write your initial unit tests before writing your functions, iteratively fixing your code and your tests until your code passes all the tests. This is called test-driven development.
Submission
This lab will not be graded so there is nothing to submit. Make sure you keep a copy of your code for future reference. There may be a future homework based on this lab. If you would like to discuss your solution or any problems you encounter while working on this lab, please come to office hours or make an appointment.