Although lisp lists allow mixing of types (and so are equivalent to binary trees), we will implement typed lists, which allow elements of only one type, using templates. We want to provide 4 functions:
- cons, which takes an element and a list, and produces another list, containing that element as its first element (and the passed list as the elements after the first element; notice we're going to share this passed list).
- car, which takes a list and returns its first element (sometimes called head).
- cdr, which takes a list and returns another list, the list minus the first element (sometimes called tail).
- isEmpty, which returns true if the list is empty, false otherwise.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template<typename T> | |
struct ConsNode { | |
public: | |
ConsNode(T car=T(), ConsList<T> cdr=ConsList<T>()):_car(car),_cdr(cdr) {} | |
private: | |
T _car; | |
ConsList<T> _cdr; | |
friend T car<>(const ConsList<T> &l); | |
friend const ConsList<T>& cdr<>(const ConsList<T> &l); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template <typename T> | |
using ConsList= std::shared_ptr<ConsNode <T> >; |
Now, cons is the function that will create a new list, given an old list and a new element (notice this does NOT change the original list at all; we want to avoid modifying the lists, so they can be shared as parts of other lists). Our cons function is going to call std::make_shared, which creates a new reference-counted chunk of memory (you can create a shared_ptr from a plain dynamically allocated pointer, but at the cost of an extra call to new). Our cons function looks like:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template<typename T> | |
ConsList<T> cons(T car, const ConsList<T>& cdr=ConsList<T>()) { | |
return std::make_shared<ConsNode<T> > (car,cdr); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template<typename T> | |
T car(const ConsList<T>&l) | |
{ | |
return l->_car; | |
} | |
template<typename T> | |
const ConsList<T>& cdr(const ConsList<T>&l) | |
{ | |
return l->_cdr; | |
} | |
template<class T> | |
bool isEmpty(const ConsList<T>&l) | |
{ | |
return !l; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template<class T> | |
std::ostream& operator<<(std::ostream& o, const ConsList<T>&l) | |
{ | |
if(!isEmpty(l)) | |
o << car(l) << " " << cdr(l); | |
return o; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template<typename T> | |
unsigned len(const ConsList<T>&l) | |
{ | |
if(!isEmpty(l)) | |
return 0; | |
return 1+len(cdr(l)); | |
} | |
template<typename T> | |
T sum(const ConsList<T>& l) | |
{ | |
if(isEmpty(l)) | |
return 0; | |
return car(l)+sum(cdr(l)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int main(void) | |
{ | |
ConsList<int> l=cons(3,cons(4,cons(7))); | |
ConsList<int> l2=cons(9,cons(5,l)); // l2 shares the last 3 elements with l | |
ConsList<int> l3=cons(11,cdr(l2)); // l3 shares with l2, and also with l | |
cout << car(l) << " " << car(cdr(l)) << endl; | |
cout << len(l2) << " " << sum(l2) << endl; | |
cout << l2 << endl; | |
cout << l3 << endl; | |
} |
Nice code! would be interesting to see how this compares to an implementation based on Boost's recursive_variant...
ReplyDeleteIn len(), "!isEmpty" needs to be "isEmpty".
ReplyDelete