In this project, you will use virtual
function
mechanism of C++.
("abc", 1, 3.2, 'a')is a list. For the purpose of this assignment, list elements can be integers, strings and other lists. For example,
("abc", (1, 2), 3)is a list where the first element is a string
"abc"
,
the second element is a list containing two integers, 1 and 2,
and the first element is an integer 3.
Possible operations on lists are cons
(takes an element
and a list and inserts the element in the beginning of list),
car
(returns the first element of the list),
cdr
(returns the list with the first element removed),
null
(returns true if the list has no elements), and
append
(takes two lists and puts the second list to the
right of the first.) Here are some examples of list operations:
cons (1, (2, 3, 4)) = (1, 2, 3, 4) cons (1, (1, 2, 3)) = (1, 1, 2, 3) cons ((1, 2), (2, 3)) = ((1, 2), (2, 3)) car ((1, 2, 3)) = 1 car (((1, 2), 3, 4)) = (1, 2) cdr ((1)) = () /* empty list, i.e., list with no elements */ cdr (((1, 2), 3, 4)) = (3, 4) null ((1, 2)) = false null (()) = true append ((1), (2, 3)) = (1, 2, 3) append (((1)), (2, 3)) = ((1), 2, 3) append ((), (2, (3))) = (2, (3))
In functional languages like Scheme or ML, lists are represented
using a dotted notation. All lists are constructed using
cons
, and a "dot" is put in place where cons
was called. For example, a list (1, 2, 3)
could be
constructed starting with an empty list ()
using
a constructor cons
as follows:
cons (1, cons (2, cons (3, ())))
i.e., first construct a list (3), then insert 2 in front of it, and then insert 1 in front of the resulting list. So, in functional languages a list (1, 2, 3) would be represented as
(1. (2 . (3. ())))Note that unlike Scheme, this
cons
does not make the copy
of the original list! .
You are to implement lists in C++. The elements consist of strings,
integers and other lists. Implement cons
, car
,
cdr
, null
and append
( append
makes a new list which consists of concatenation of the
previous lists). In addition,
implement operations print
and clone
.
The print
operation should produce
the ``dotted'' notation form of lists. clone
makes
a copy of the list and returns a reference to it.
The program skeleton appears below. It is broken down into two files here, the file containing class definitions and implementation of member functions, and the file containing main program. In your projects, please break up the solutions into at least 3 files.
// File classdef.h // This file contains a definition and an implementation for // lists. class Object; typedef Object* PObject; // we do this so that we can define // a pointer to class Object class Object { /* an abstract class */ public: virtual PObject clone() = 0; virtual ~Object() {}; friend ostream &operator<<(ostream &, const Object &); private: virtual ostream &printTo(ostream &o) const=0; }; ostream &operator << (ostream &o, const Object &s) { return s.printTo(o); } // You will need to provide definitions for classes // String and Int, and implementations for classes // String, Int and List. class String; class Int; class List; typedef List* PList; // This is the definition of class List class List: public Object { public: List(); ~List(); PObject car() const; List& cons(PObject); List& cdr() const; int null() const; ostream &printTo(ostream &o) const; PObject clone(); private: PObject car_part; PList cdr_part; }; // Notice that append is not a member function! List& append (List* X, List* Z) { }
And now the file which contains main
:
// main.C. This file contains test cases for the // implementation of lists. #include < iostream.h > #include "classdef.h" int main() { String V1("Hello"); Int V2(7); Object *p1 = &V1, *p2 = &V2; List L1; List L2; List L3 = L2.cons(&Int(1)).cons(&String("Two")).cons(&Int(3)).cons(&String("Four")).cons(&Int(5)).cons(&Int(6)); List L4 = L2.cons(&Int(25)).cons(&String("Fleas")).cons(&String("has")).cons(&Int(24)); List L5 = append(&L3,&L4); for(; !L3.null(); L3= L3.cdr()) { cout << *L3.car() << endl << L3 << endl << L3.cdr()t << endl; } cout << L3 << endl << endl << endl; cout << V1 << endl; cout << V2 << endl; p1 = V1.clone(); cout << *p1 << endl; p2 = V2.clone(); cout << *p2 << endl; cout << "(L1.cons(&V1))=" << (L1.cons(&V1)) << endl; cout << "(L1.cons(p1))=" << (L1.cons(p1)) << endl; p1 = (L1.cons(&V1)).clone(); p2 = (L1.cons(&V2)).clone(); cout << "append((PList)p1,(PList)p2)=" << append((PList)p1,(PList)p2) << endl; cout << "(L1.cons(&V2).cons(&V1))=" << (L1.cons(&V2).cons(&V1)) << endl; if ( (L1.cons(&V2).cdr()).null() ) cout << "null list\n"; cout << L4 << endl; cout << L5 << endl; }Your program should produce the following output.
Int: 6 (Int: 6 . (Int: 5 . (String: "Four" . (Int: 3 . (String: "Two" . (Int: 1 . ( ))))))) (Int: 5 . (String: "Four" . (Int: 3 . (String: "Two" . (Int: 1 . ( )))))) Int: 5 (Int: 5 . (String: "Four" . (Int: 3 . (String: "Two" . (Int: 1 . ( )))))) (String: "Four" . (Int: 3 . (String: "Two" . (Int: 1 . ( ))))) String: "Four" (String: "Four" . (Int: 3 . (String: "Two" . (Int: 1 . ( ))))) (Int: 3 . (String: "Two" . (Int: 1 . ( )))) Int: 3 (Int: 3 . (String: "Two" . (Int: 1 . ( )))) (String: "Two" . (Int: 1 . ( ))) String: "Two" (String: "Two" . (Int: 1 . ( ))) (Int: 1 . ( )) Int: 1 (Int: 1 . ( )) ( ) ( ) String: "Hello" Int: 7 String: "Hello" Int: 7 (L1.cons(&V1))=(String: "Hello" . ( )) (L1.cons(p1))=(String: "Hello" . ( )) append((PList)p1,(PList)p2)=(String: "Hello" . (Int: 7 . ( ))) (L1.cons(&V2).cons(&V1))=(String: "Hello" . (Int: 7 . ( ))) null list (Int: 24 . (String: "has" . (String: "Fleas" . (Int: 25 . ( ))))) (Int: 6 . (Int: 5 . (String: "Four" . (Int: 3 . (String: "Two" . (Int: 1 . (Int: 24 . (String: "has" . (String: "Fleas" . (Int: 25 . ( )))))))))))
Submit (electronically) a tar file with
Note that we will rerun your program with, so please do not change the output of your program!!!!!
Bring a paper copy with your programs, test cases, output, etc. to class
Marking scheme for this project is as follows:
Correctness | 55 | The program gives the correct output. |
Documentation | 10 | The program should contain explanations for each class, each function and the entire program. |
Testing | 10 | Has the program been tested appropriately? To test the program, generate various Main programs with the same operations as those in the sample Main, build the executable and check the output. |
Style | 25 | The program should be written using object-oriented programming style and work efficiently, avoiding extra computation where appropriate. |
CC
or
g++
compilers on CDF. Also, make sure that your Makefile
uses the appropriate compiler. We will use your Makefiles to build
executables and run them.