Object-Oriented Programming: Stacks
Object-Oriented Programming: Stacks
Goals: This laboratory exercise reviews strategies of problem
solving discussed in this class. Then, building on recent work, the lab
introduces stacks as an example of a useful class.
Handling Complexity: Since many computer applications address
complex problems, problem-solving with computers must include a
consideration of the handling of complexity. Already in this course, we
have seen several such strategies and use a variety of language
capabilities.
-
Solutions can be built up using a sequence of procedures, where each
procedure accomplishes a relatively simple task.
-
Local variables and parameters allow work within a procedure
to focus on a narrow, tightly controlled task even if the procedure will be
used within a more complex problem.
-
Using recursion, processing can focus on relatively simple base
cases and the reduction of complex cases to simpler ones.
-
Data abstraction allows data to be viewed in logical groups and
structures, such as lists, vectors, association lists, and records.
-
Procedural abstraction allows general procedures to be defined
abstractly to cover many types of cases; mechanisms for variable arity
procedures, procedure parameters, and currying give flexibility and allow
general procedures to be tailored to specific needs.
-
Libraries allow commonly used operations to be developed once and
reused in other applications.
-
Classes and objects package data and operations together in a
convenient package that can be used in various applications.
Each of these topics allow us to organize a complex problem or task into
relatively manageable parts and then to focus on those parts.
Note that several of the items lists involve abstraction: we think of
processing at a relatively high level, with details pushed aside to a
separate procedure, structure, or file.
For example, association lists allow a natural matching of keyword and
data, so we can focus upon the storage and retrieval of these information
pairs. The assoc procedure retrieves relevant data, and we need
not be bothered about the details of this retrieval.
-
Identify how you have used each of the above approaches or capabilities in
the solving of at least two specific problems during this course.
Stacks as ADTs: Stacks provide a common and useful example of
classes and objects.
Conceptually, the stack class or abstract data type mimics the information
kept in a pile on a desk. Informally, we first consider materials on a
desk, where we may keep separate piles for bills that need paying,
magazines that we plan to read, and notes we have taken. These piles of
materials have several properties. Each pile contains some papers
(information). In addition, for each pile, we can do several tasks:
-
Start a new pile,
-
Place new information on the top of a pile,
-
Take the top item off of the pile,
-
Read the item on the top, and
-
Determine whether a pile is empty. (There may be nothing at the spot where
the pile should be.)
These operations allow us to do all the normal processing of data at a
desk. For example, when we receive bills in the mail, we add them to the
pile of bills until payday comes. We then take the bills, one at a time,
off the top of the pile and pay them until the money runs out.
When discussing these operations it is customary to call the addition of an
item to the top of the pile a Push operation and the deletion of an
item from the top a Pop operation.
More formally, a stack is defined as a class that can
store data and that has the following operations:
-
Make-Stack
Create a new, empty stack object.
-
Empty
Empty returns true or false (#t or #f), depending
upon whether the stack contains any items or not.
-
Push
Push adds the specified item to the top of the stack.
-
Pop
If the stack is not empty, Pop removes the top item from the stack,
and this item is returned.
If the stack is empty, nothing is returned, and an error is reported.
-
Top
If the stack is not empty, the top item is returned, but the contents of
the stack are not changed.
If the stack is empty, nothing is returned, and an error is reported.
This specification says nothing about how we will program the various stack
operations; rather, it tells us how stacks can be used. We can also infer
some limitations on how we can use the data. For example, stack operations
allow us to work with only the top item on the stack. We cannot look at
other pieces of data lower down in the stack without first using Pop
operations to clear away items above the desired one.
A Push operation always puts the new item on top of the stack, and
this is the first item returned by a Pop operation. Thus, the last
piece of data added to the stack will be the first item removed.
Classes, Objects, and Messages: One approach to problem solving
involves an initial focus on data elements, operations on those elements,
and relationships among data. Within computer science, such an approach
motivates the viewpoint of object-oriented programming or
OOP. When working within a OOP framework, it is useful to
distinguish between the general notion of a class and
specific uses of the class with definite data values, which constitutes an
object.
Utilizing the notion of abstraction, we think of objects as self-contained
entities, just as we might consider each pile on a desk as an separate,
independent collection of material. To support this image, processing
involving objects consists of sending messages to the objects, letting the
object react to the object in an appropriate way, and receiving responses
back. Within an OOP context, a mechanism for interpreting messages is
called a method. For example, if our desk had two piles -- one for
magazines and one for bills, we might have the following interaction:
> (define mag-pile ... ) ; make stack for magazines
; (details of call dependent on implementation)
> (define bill-pile ... ) ; make stack for bills
; (details of call dependent on implementation)
> (bill-pile 'push! "mortgage") ; send push message to bill-pile
> (bill-pile 'push! "doctor's bill")
> (bill-pile 'push! "credit card")
> (bill-pile 'empty?) ; send empty? message to bill-pile
#f ; response from empty? message
> (mag-pile 'empty?)
#t
> (mag-pile 'push! "Communications of the ACM - April 1997")
> (mag-pile 'push! "CS Education Bulletin - Spring 1998")
> (bill-pile 'top) ; send top message to bill-pile
"credit card" ; data returned as message response
> (mag-pile 'top)
"CS Education Bulletin - Spring 1988"
> (bill-pile 'pop!)
"credit card"
> (bill-pile 'top)
"doctor's bill"
Stacks in Scheme: To implement a class within Scheme,
we may proceed following the recent lab on
Abstract Data Types. The simpest approach is to
maintain an internal list within the abstract data type or class. With
this approach, push and pop operations insert or delete an
item from the front of the list, the empty operation tests if the
list is empty, and the top operation returns the head of the list.
-
Write a stack class, following the implementation described above.
-
Check your implementation using the script given earlier in this lab.
-
Expand the code for the stack ADT implementation by adding
-
a size method which will return the number of items currently on
the stack,
-
a print method which will print all of the current elements on the
stack, and
-
an nth method which takes one parameter (an index) and returns the
item at that position from the top in the current stack.
Work to be turned in:
-
Comments for part 1.
-
Code, with test runs, for parts 2 and 4.
(The test runs for part 2 should include the script of part 3.)
This document is available on the World Wide Web as
http://www.math.grin.edu/~walker/courses/153.sp00/lab-stacks.html
created April 28, 1997
last revised January 16, 2000