Cplusplus4c Ebooks Stlbook
Cplusplus4c Ebooks Stlbook
Ira Pohl
This eMatter edition is related to C++ Distilled: A Concise ANSI/ISO Reference and Style Guide, a paperful book by Addison-Wesley Publishing Company, Inc 1997.
ISBN 0-201-69587-1
Ira Pohl, Ph.D., is a professor of Computer Science at the University of California, Santa Cruz. He has over 30 years of experience as a software methodologist. His teaching and research interests are in the areas of artificial intelligence, programming languages, practical complexity problems, heuristic search methods, deductive algorithms, and educational and social issues. He originated error analysis in heuristic search methods and deductive algorithms. He has lectured at Berkeley, Stanford, the Vrije University in Amsterdam, the Courant Institute, Edinburgh University in Scotland, and Auckland University in New Zealand. When not programming, he enjoys riding bicycles in Aptos, California, with his wife Debra and daughter Laura. Iras web address is http://www.cse.ucsc.edu/~pohl/. He can be reached via email at pohl@cse.ucsc.edu.
He is also coauthor will Charlie McDowell of the Addison-Wesley publication due out in Fall of 1999: Java by Dissection: The Essentials of Java Programming
He is the sole author of Addison-Wesley or Benjamin Cummings publications: C++ for C Programmers C++ for Pascal Programmers C++ for Fortran Programmers Turbo C++ Object Oriented Programming Using C++ C++ Distilled
His first book, coauthored with Alan Shaw, was a pioneering text on computer science (Computer Science Press, 1981): The Nature of Computation: An Introduction to Computer Science
eMatter publications available through FatBrain: The C++ Bookshelf: Distilled Object Oriented Programming Using C++ C++ for Pascal Programmers Other eMatter books forthcoming.
Contents
1 2
Generic Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Iterators and Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.1 A Visitation Example: Accumulate . . . . . . . . . . . . . . . . . . . . 5 Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Templates . . . . . . . . . . . . . . 4.1 Template Parameters 4.2 Function Template . . 4.3 Friends . . . . . . . . . . . 4.4 Static Members . . . . . 4.5 Specialization . . . . . . .. .. .. .. .. .. ... ... ... ... ... ... .. .. .. .. .. .. ... ... ... ... ... ... .. .. .. .. .. .. ... ... ... ... ... ... .. .. .. .. .. .. ... ... ... ... ... ... ... ... ... ... ... ... .. .. .. .. .. .. ... ... ... ... ... ... . 11 . . 12 . . 13 . . 15 . . 15 . . 16
3 4
5 6
STL: Basics and the Container vector . . . . . . . . . . . . . . . . . . . 21 STL: Containers . . . . . . . . . . . . . . . . . 6.1 Containers . . . . . . . . . . . . . . . . 6.1.1 Sequence Containers . . 6.1.2 Associative Containers 6.1.3 Container Adaptors . . . STL: Iterators . . . . . . . . . . . . . . . . . 7.0.1 Iterator Categories. . 7.0.2 Istream_iterator . 7.0.3 Ostream_iterator . 7.0.4 Iterator Adaptors . . . .. .. .. .. .. ... ... ... ... ... ... ... ... ... ... .. .. .. .. .. .. .. .. .. .. ... ... ... ... ... ... ... ... ... ... .. .. .. .. .. .. .. .. .. .. ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... .. .. .. .. .. .. .. .. .. .. ... ... ... ... ... ... ... ... ... ... . . . . . . . . . . 26 . 27 30 31 34 38 39 40 41 42 46 46 49 51 55
STL: Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.0.1 Sorting algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . 8.0.2 Nonmutating Sequence Algorithms . . . . . . . . . . . . 8.0.3 Mutating Sequence Algorithms . . . . . . . . . . . . . . . 8.0.4 Numerical Algorithms . . . . . . . . . . . . . . . . . . . . . . .
vi
Contents
10
String Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 10.1 Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 10.2 Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 10.3 Global Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Supplemental Programs . . . . . . . . . . . . . . . . . . . . . . 12.1 Copy Using Conversion Compatible Types . . 12.2 Generic Stack . . . . . . . . . . . . . . . . . . . . . . . . . . 12.3 Reverse Iterator . . . . . . . . . . . . . . . . . . . . . . . . .. .. .. .. ... ... ... ... .. .. .. .. . . 76 . . . 76 . . . 78 . . . 80
11 12
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Preface
STL Distilled and Generic Programming is an a eMatter companion volume to C++ Distilled: Addison-Wesley. It updates and adds material on templates, generic programming and STL as found in ANSI C++. It supplements and brings up-to-date existing literature. This eMatter book is a concise road map and style guide to generic programming in C++. It distills key ideas and practice for the ANSI standard C++ language and includes many programming tips. It is easily used with any C++ programming book (see Chapter 11, References, on page 74, for a selection), but is especially suitable when used with one of the authors books, such as Object-Oriented Programming Using C++, 2nd Edition (OPUS 97) or C++ for C Programmers, 3rd edition. (P 99). Each section has the syntax, semantics, and examples of the language element. There are style and programming tips at the end of most sections. Examples have a consistent professional style to be mimicked by programmers. This eMatter book in conjunction with C++ Distilled is a distillation of the C++ ANSI standard, which is approximately 700 detailed technically dense pages, and rather overwhelming. Fortunately most programmers do not need such detail; indeed, many of the features are highly specialized and little used. Most programmers need to be able to quickly review some syntax or semantics they have not recently used. C++ has had many recent additions, including STL. These can be used readily by someone already proficient in basic C++, but most books have yet to treat these topics. This eMatter book can provide a handy guide to this important library. Most programming is done in my imitation of existing code and idioms. These examples use my prescriptions and programming tips (Dr. Ps Prescriptions) which are a distillation of considerable professional practice.
Preface
R X
Prescription Discussion
Style emphasizes clarity and community norms. Consistency, while the hobgoblin of small minds, is well suited to large computer codes.
Acknowledgments
This eMatter was developed as an extension and to C++ Distilled. That book benefited from reviewers Ed Lansinger of General Motors Corporation; Henry A. Etlinger of Rochester Institute of Technology; Glen Deen of Deen Publications, Inc.; Michael Keenan of Columbus State University; and David Gregory. Most importantly, I thank Debra Dolsberry for her invaluable help in the technical editing of this eMatter book, and her careful testing of the code.
Dedication
To Alexander Stepanov and Donald Knuth, who created generic programming and the detailed analysis of best algorithms, respectively.
Chapter 1
Generic Programs
A key problem in programming is programmer productivity. An important technique is code reuse. Generic programming is a critical methodology for enhancing code reuse. Generic programming is about code that can be used over a wide category of types. In C++ there are three different ways to employ generic coding techniques: void* pointers, templates, and inheritance. This chapter will show a simple use of each of these methods. This eMatter book will largely concern C++ templates and how they are employed in STL, the C++ standard template library. We will start with a simple example of code that can benefit from genericity. This is the everyday application of assigning the contents of one array to a second array.
Generic Programs
int a[10], b[10]; double c[20], d[20]; transfer(a, b, 10); transfer(c, d, 20); //works fine //syntax error
C++ has a void pointer type that can be used to create generic code. Generic code is code that can work with different types.
C++ has template functions that can be used to create generic code.
Generic Programs
The template function requires that the type be properly instantiated. It does not allow two distinct types to be used in this form of array transfer. It continues to provide type safety which is important to program correctness.
R X
Generic Programs
Prescription Discussion
In this eMatter book we follow the traditional C and C++ style pioneered by Bell Laboratories programmers, such as Kernighan, Ritchie and Stroustrup (KR 88, GRAY 91, ABC 95, P 97). Several elements of this style can be seen in the our programs. Beginning and ending braces for function definitions line up under each other and under the first character of the function definition. Beginning braces after keywords, such as do and while, follow the keyword with the ending brace under the first character of that line. This style is in widespread use and makes it easy for others to read your code. The style allows us to distinguish key elements of the program visually, enhancing readability. Style should aim for clarity for both ourselves and others who need to read our code. Cleverness by its nature is usually obscure. This is the enemy of clarityhence Kernighan and Plaugers maxim Write clearlydont be too clever. Also, inconsistent style tends to obscure. While void* genericity has certain advantages, such as smaller executables than template genericty, it can be more error-prone and less efficient. It can be difficult to write and test generic code from scratch. Concreteness is a great aid to the program developer. Pick a type that represents the archetypal case. In our example this was the int array. Develop the code for this case and test, making sure its correct. Finally convert this to template code and retest with selected types.
Chapter 2
Iterators and Containers
A container is a data structure that is used to contain a large number of values. The prototypical container is the array. Other familiar containers include the list, queue, stack and map. An iterator is a device for traversing a container. The indexing of an array is a way to sequence through or randomly visit array elements. The C++ pointer is a prototypical iterator.
any types that need to be generic and substitute the template arguments for the concrete types. Retest the code using several representative types.
R X
2.1
Prescription Discussion
As in STL, we use a beginning iterator and a sentinel iterator to pass in a range for the traversal of a container. This is a very flexible scheme and is highly efficient. Its flexibility comes from the fact that any contiguous subsection of the container can be specified. C++ is designed to be template friendly. Code, when designed as a template, benefits from greater abstraction than corresponding specialized code. What I mean by this is that programmers are normally overly clever. This leads to hard to maintain code with possibly subtle bugs. Generic code must be correct over a wide range of types and cannot indulge in cleverness. Iterators avoid the commitment to a particular container type. In contrast using indices to access arrays does not allow for pointer traversal as used in list and tree containers. Generalization benefits by avoiding commitments. Generalization is the heart and brilliance of the STL library.
Algorithms
Chapter 3
Algorithms
STL is a library of generic algorithms. These algorithm represent best practice (K 97). Given a particular problem such as internal sorting for a sequence container supporting random access, such as an array, what is known is that quicksort is an algorithm with good behavior. The problem for STL is how to capture such an algorithm in its most general form without degrading its performance. An example of a best practice algorithm is the following code for finding the minimum and maximum element in a container using fewest number of comparisons (P 72). This example was developed when I was trying to understand more complicated examples of sorting and led to one of the first formal proofs based on an adversary strategy. It is intuitively obvious, but it is still hard to see why it is always best in terms of number of comparisons. We will develop the algorithm in pieces. For its history and influence in the analysis of algorithms (K 98) //minimum of an array of values int min(int* begin, int* end) { int minimum = *begin; while(++begin != end) if( minimum > *begin) minimum = *begin; return minimum; } This is our garden variety find the minimum of a sequence algorithm. As with other STL inspired algorithms we write it in terms of iterator parameters. For n elements it requires n-1 comparisons. Symmetrically we can immediately write out the maximum algorithm.
//maximum of an array of values int max(int* begin, int* end) { int maximum = *begin; while(++begin != end) if( maximum < *begin) maximum = *begin; return maximum; } Now we wish to return from our analysis of an unsorted sequence the minimum and maximum element of a sequence. Do this as a return value requires a new type the int_pair. class int_pair{public: int first, second;}; Again this is inspired by type pair<> found in the STL library, where among its uses are its ability to store map related values (STL 96). A first attempt at producing a min-max algorithm would be to use both the minimum and maximum algorithms. This would take 2n - 2 comparisons. We can see that certain comparisons are redundant. in the most elementary case, if we find a minimum we know it will not be simulataneously the maximum unless it is the sole value in the sequence. Indeed any comparison that leads to a minimum produces an element smaller than a maximum, and this element need not be tested for being a maximum. This insight leads to splitting the original sequence in to 2 sequences that contain repectively candidate minima and maxima. The code for this is as follows: void swap(int* p, int* q){int temp = *p; *p = *q; *q = temp;} //assume an even number of elements int_pair min_max(int* begin, int* end, int* middle) { int* mbegin = begin; int* mmiddle = middle; int_pair ans; while (mbegin != middle) { if ( *mbegin > *mmiddle) swap ( mbegin, mmiddle); mbegin++; mmiddle++; }
10
Algorithms
ans.first = min(begin, middle); ans.second = max(middle, end); return ans; } //Test Program int main() { int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int_pair mm; mm = min_max(a, a + 10, a + 5); cout << " minimum and maximum is " << mm.first << " "<< mm.second<< endl; } This leads to performance that is 3n -2 comparisons. The original n elements are compared pair wise and divided into two groups of n/2 elements. This takes n/2 comparisons; the minimum is found in n/2-1 comparison and likewise the maximum. We leave as an exercise what is needed for an odd number of elements. Notice this algorithm can be rewritten for any type that allows comparison. Also the original ordering of elements may be changed by the algorithm. This means the code is mutating. These categories of mutating and sorting are used by STL. It remains to templatize the algorithm in order to make it generic. We leave this as an exercise.
R X
Prescription Discussion
The study of algorithms is central to good coding. Knuth in his three volume Art of Computing has exhaustively presented analysis and design of datastructures and algorithms. Studying these volumes for the modern computer scientist and programmer is analagous to an ancient geometer studing Euclids eements. Without this study you have no basis to understand what a good algorithm is or how to develop one. As fast as computers become, there is no substitute for using a best algorithm in many instances. Resource use is almost always an issue somewhere in serious code. To appreciate STL as an achievement requires the understanding of best algorithm for different circumstance.
Templates
11
Chapter 4
Templates
The keyword template is used to implement parameterized types. Rather than repeatedly recoding for each type, the template feature allows instantiation to generate code automatically for each type. The following code is a simple implementation of a generic stack container class.
12
Templates
The class identifier arguments are instantiated with a type. Other argument declarations are instantiated with constant expressions of a nonfloating type, and can be a function or address of an object with external linkage as shown in the following code. In file array.cpp template<class T, int n > class array_n { private: T items[n]; }; array_n<complex, 1000> w;
Member function syntax, when external to the class definition, is as follows. template <class T> T& stack<T>::pop() { return(item[top--]); } The class-name used by the scope resolution operator includes the template arguments, and the member function declaration requires the template declaration as a preface to the function declaration.
4.2
Function Template
13
The default parameters can be instantiated when declaring variables, or can be omitted, in which case the defaults will be used. Templates can use the keyword typename in place of class. For example: template<typename T = double, double* ptr_dbl> This allows the template code to use a pointer to double argument. Ordinary floating-point arguments are not allowed, only pointer and reference to floating-point arguments are allowed. A template argument can also be a template parameter. For example: template<typename T1, template<class T2> class T3> This allows very sophisticated metatemplatestemplates instantiated with templatesto be coded. Libraries such as STL can use such features.
14
Templates
//ANSI C++ but unavailable in many current compilers template <class T, int n> T foo() { T temp[n]; } foo<char, 20>(); //use char, 20 and call foo
A function template is used to construct an appropriate function for any invocation that matches its arguments unambiguously: swap(i, j); swap(c1, c2); swap(i, ch); //i j int - okay //c1, c2 complex - okay //i int ch char - illegal
The overloading function selection algorithm is as follows. Overloaded Function Selection Algorithm 1. Exact match with trivial conversions allowed on a nontemplate function. 2. Exact match using a function template. 3. Ordinary argument resolution on a nontemplate function. In the previous example, an ordinary function declaration whose prototype was void swap(char, char); would have been invoked on swap(i, ch).
4.3
Friends
15
4.3 Friends
Template classes can contain friends. A friend function that does not use a template specification is universally a friend of all instantiations of the template class. A friend function that incorporates template arguments is a friend only of its instantiated class: template <class T> class matrix { public: friend void foo_bar(); friend vect<T> product(vect<T> v); };
//universal //instantiated
a, b; c;
The static variables foo<int>::count and foo<double>::count are distinct. The variables a.count and b.count reference foo<int>::count, but c.count references foo<double>::count. It is preferable to use the form foo<type>::count since this makes it clear that the variable referenced is the static variable.
16
Templates
4.5 Specialization
When the template code is unsatisfactory for a particular argument type it can be specialized. A template function overloaded by an ordinary function of the same typethat is, one whose list of arguments and return type conform to the template declarationis a specialization of the template. When the specialization matches the call, then it, rather than code generated from the template, is called. void maxelement<char*>(char*a[],char* &max,int size); //specialized using strcmp() to return max string This would be a specialization of the previously declared template for template<class T>maxelement(). Class specializations are also possible, as in: class stack<foobar_obj> { /*specialize for foobar_obj */ };
Templates Program
In file vect_it.cpp //templates for vect with associated iterator class #include #include <iostream.h> <assert.h>
//for assert
template <class T> class vect_iterator; template <class T> class vect { public: //constructors and destructor typedef T* iterator; explicit vect(int n = 10); //default constructor vect(const vect& v); //copy constructor vect(const T a[], int n); //from array ~vect() { delete [] p; }
4.5
Specialization
17
iterator begin(){ return p; } iterator end(){ return p + size; } T& operator[](int i) const; vect& operator=(const vect& v); friend vect operator+(const vect& v1, const vect& v2); friend ostream& operator<<(ostream& out, const vect<T>& v); friend class vect_iterator<T>; private: T* p; int size; };
//default constructor template <class T> vect<T>::vect(int n): size(n) { assert(n > 0); p = new T[size]; assert(p != 0); } //copy constructor template<class T> vect<T>::vect(const vect<T>& v) { size = v.size; p = new T[size]; assert (p != 0); for (int i = 0; i < size; ++i) p[i] = v.p[i]; } //Initializing vect from an array template<class T> vect<T>::vect(const T a[], int n) : size (n) { assert (n > 0); p = new T[size]; assert (p != 0); for (int i = 0; i < size; ++i) p[i] = a[i]; }
18
Templates
//overloaded subscript operator template<class T> T& vect<T>::operator[](int i) const { assert (i >= 0 && i < size); return p[i]; } //overloaded output operator template<class T> ostream& operator<<(ostream& out, const vect<T>& v) { for (int i = 0; i <= (v.size-1); ++i) out << v.p[i] << '\t'; return (out << endl); } template<class T> vect<T>& vect<T>::operator=(const vect<T>& v) { assert(v.size == size); for (int i = 0; i < size; ++i) p[i] = v.p[i]; return *this; } template<class T> vect<T> operator+(const vect<T>& v1, const vect<T>& v2) { assert(v1.size == v2.size) ; vect<T> sum(v1.s); for (int i = 0; i < s; ++i) sum.p[i] = v1.p[i] + v2.p[i]; return sum; }
4.5
Specialization
19
template<class T> void init_vect(vect<T>& v, int start, int incr) { for (int i = 0; i <= v.ub(); ++i) { v[i] = start; start += incr; } } int main() { vect<double> v(5), t(5); vect<double>::iterator p ; int i = 0; for (p = v.begin() ; p != v.end(); ++p) *p = 1.5 + i++; do { --p; cout << *p << " , "; } while (p != v.begin()); t = v; //test assignment v = v + t; //test addition for (p = v.begin() ; p != v.end(); ++p) cout << *p << " , "; cout << endl; }
R X
20
Templates
Prescription Discussion
Templates are especially good for code that is repeatedly required with different types. Container class code is usefully generalized by coding with templates. A container is an object whose primary purpose is to store values. A classic example of a container is a stack. Templates allow such code to be reused over arbitrary type with type-safety that is checked at compile-time. Before templates were used much generic code in C++ was written using void* arguments to functions. This generic pointer type can accept any specific pointer type as an argument. This code can largely be replaced with templates. The code is again compile-time type-checked. Also, template functions need not manipulate arguments indirectly with pointers. Template code is easily developed through generalization of a specific typical case. Develop code with the specific case first; for example, develop code for a stack of integers. Only after all of this code is satisfactory and debugged should it be converted to a general template. Then retest this code over a selection of data types that might represent typical template use. Templates and inheritance are both techniques for reusing code. When both techniques are possibilities for developing classes templates are preferred to inheritance because they are usually more efficient and lead to simpler class design. Inheritance couples classes.
21
Chapter 5
STL: Basics and the Container vector
In this section we wish to give the basic ideas behind STL. We will use as our principle example the container class vector and its use. Arguably the most used container class is the vector. Indeed we recommend along with other authorities (S 97) that it wherever possible replace raw arrays. The vector class is safer than then a native array and is more flexible. There might be a slight tradeoff in use of resources, but in most applications it is the superior data structure.
Vector Program
In file test_vector.cpp //Simple STL vector program #include <iostream> #include <vector> using namespace std; int main () { vector<int> v(100); //100 is the vector's size for (int i = 0; i < 100; i++) v[i] = i; for (vector<int>::iterator p = v.begin(); p != v.end(); p++) cout << *p << '\t'; } The STL container vector is used in place of an ordinary int array. The first for-statement is written in exactly the same manner as a C++ loop on ordinary data. The second for-statement is written using the iterator p. An iterator behaves as a pointer. STL provides the member functions begin() and end() as initial and terminal position values for the container. Note that end() returns the iterator position (or address) one past the last element of the container. Thus, end() is a guard location, or value signaling that you are finished traversing the container.
22
The iterator is conceptually a pointer. In some cases the template generates a pointer. Regardless of what the template generates, the user of a vector can code as if he had pointer or array indexing as is the case for raw C++ arrays. The vector<> container type is better than an array in several ways. It has more functionality. In the previous example we had begin and end locations available. We did not have to retain array bounds. The vector<> type resizes automatically. We see some of these properties in the following simple example.
Vector2 Program
In file test_vector2.cpp #include <iostream> #include <vector> using namespace std; int main() { int data[10] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 }; vector<int> v; vector<int>::iterator p; cout << " size of v is " << v.size(); cout << " maximum size of v is " << v.size() << endl; for (int i = 0; i < 10; i++) v.push_back(data[i]); for (p = v.begin(); p != v.end(); p++) cout << *p << " , " ; cout << endl; cout << " size of v is " << v.size(); cout << " maximum size of v is " << v.max_size()<< endl; } Notice the vector v is not declared with any parameters. It will start as a size 0 vector. When we use push_back() we automatically add elements to the end of v and increase its size as needed. Try this program and see what your system prints for the initial v.size() and the final v.size() and v.max_size(). The vector is the prototypical sequence container. It can be indexed through. Indeed its elements can be accessed randomly making it very flexible. as we shall see the two other main sequence containers, list and deque, share many but not all of the vector containers properties.
23
These are typedefs provided for thervector container class. For example, vector<char>::value_type means a character value is stored in the vector container. Such a container could be traversed with a vector<char>::iterator. Vectors allow equality and comparison operators. They also have an extensive list of standard member functions. STL Vector<> Members
vector<>::vector<>() vector<>::vector<>(c) c.begin() c.end() c.rbegin() c.rend() c.size() c.max_size() c.empty() c.swap(d) default constructor copy constructor beginning location of vector<> c ending location of vector<> c beginning for a reverse iterator ending for a reverse iterator number of elements in vector<> largest possible size true if the vector<> is empty swap two vector<>s
24
So far we have the container vector and the traversal scheme based on iterators. But we also get a variety of algorithms that can be used with the vector container. In the following example we will show how some of these work.
Vector_Algoprithm Program
In file test_vector_algoritm.cpp #include <iostream> #include <vector> #include <algorithm> #include <numeric> using namespace std; int main() { vector<int> v(10000); vector<double> w(10000);
generate(v.begin(), v.end(), rand);//use rand() to initialize cout << "min element is = " << *min_element(v.begin(), v.end()) << endl; cout << "min element is = " << *max_element(v.begin(), v.end()) << endl; for(int i = 0; i < v.size(); i++) w[i] = v[i] / static_cast<double>(RAND_MAX); double average = accumulate(w.begin(), w.end(), 0.0)/ w.size(); cout << "average is " << average << endl; sort(v.begin(), v.end()); } In this example we can use generic algoritms to find the maximum and minimum element in a large vector. We can generate values and assign them to the elements in
25
the sequence. In the above example we use the pseudorandom number generator rand() from the library. We could as well use our own function. While finding the average element value is not directly in the STL library, accumulate() can readily be used to produce the sum of the elements and be divided by container.size() to find the average. the sort routine works only on elements that can be compared. It is O(n ln(n)) and is a version of quicksort optimized for STL containers.
Prescription Discussion
Vectors are better in almost all regards to arrays. The possible small efficiency or resource losses in some situations are not worth using arrays for. Using vectors are 90% of the value of STL. So if you go no further than mastering vector manipulation, you will gain the largest value for study effort. One is always tempted to design your own version of an algorithm. There is the psychological value of authorship. You need to resist this, as STL reuse technology is very effective. Others can readily maintain or extend such code. You will be more effective. Special cases may exist where it is desireable to use your own form of a sort or accumulate algorithm, but these are rare in professional practice. It is possible to write algorithms that just pass in the vector and process it. These algorithms will be shown in some of our examples, but it is not STL style. STL style is to use iterator ranges. This is efficient and flexible. Its only downside is that it requires an extra argument over passing in the container and assume that the range is implicitly (begin(), end()).
26
STL: Containers
Chapter 6
STL: Containers
The standard template library is the C++ standard library providing generic programming for many standard data structures and algorithms. The library provides containers, iterators, and algorithms that support a standard for generic programming. We present a brief description emphasizing these three components. The library is built using templates and is highly orthogonal in design. Components can be used with one another on native and user-provided types through proper instantiation of the various elements of the STL library (STL 96 and STLP 96). Different header files are required depending on the system. Our examples conform to the ANSI standard and are encapsulated in namespace std.
void print(const list<double> &lst) //using an iterator { //to traverse lst list<double>::const_iterator p; for (p = lst.begin(); p !=lst.end(); ++p) cout << *p << '\t'; cout << endl; }
6.1 int main() { double w[4] = { 0.9, 0.8, 88, -99.99 }; list<double> z; for (int i = 0; i < 4; ++i) z.push_front(w[i]); print(z); z.sort(); print(z); cout << "sum is " << accumulate(z.begin(), z.end(), 0.0) << endl; }
Containers
27
Here, a list container is instantiated to hold doubles. An array of doubles is pushed into the list. The print() function uses an iterator to print each element of the list in turn. Notice that iterators work like pointers. They have standard interfaces that include begin() and end() member functions for starting and ending locations of the container. Also, the list interface includes a stable sorting algorithm, the sort() member function. The accumulate() function is a generic function in the numeric package that uses 0.0 as an initial value and computes the sum of the list container elements by going from the starting location to the ending location; in the above by going from z.begin() to z.end().
6.1 Containers
Containers come in two major families: sequence and associative. Sequence containers include vectors, lists, and deques; they are ordered by having a sequence of elements. Associative containers include sets, multisets, maps, and multimaps; they have keys for looking up elements. The map container is a basic associative array and requires that a comparison operation on the stored elements be defined. All varieties of container share a similar interface.
28
STL: Containers
STL Typical Container Interfaces Constructors, including default and copy constructors Element access Element insertion Element deletion Destructor Iterators Containers are traversed using iterators. These are pointer-like objects that are available as templates and optimized for use with STL containers.
6.1
Containers
29
All container classes have these definitions available. For example, if we are using the vector container class, then vector<char>::value_type means a character value is stored in the vector container. Such a container could be traversed with a vector<char>::iterator. Containers allow equality and comparison operators. They also have an extensive list of standard member functions. STL Container Members
CAN::CAN() CAN::CAN(c) c.begin() c.end() c.rbegin() c.rend() c.size() c.max_size() c.empty() c.swap(d) default constructor copy constructor beginning location of CAN c ending location of CAN c beginning for a reverse iterator ending for a reverse iterator number of elements in CAN largest possible size true if the CAN is empty swap two CANs
30
STL: Containers
6.1
Containers
31
The five-element vector v is initialized with the value 6. The deque d is initialized with values taken from the array data. The insert() member function places the v values in the specified range v.begin() to v.end() at the location d.begin(). Sequence classes will be designated as SEQ in the following description of their interface; these are in addition to the already described CAN interface. STL Sequence Members
SEQ::SEQ(n, v) SEQ::SEQ(b_it, e_it) c.insert(w_it, v) c.insert(w_it, v, n) c.insert(w_it, b_it, e_it) c.erase(w_it) c.erase(b_it, e_it) n elements of value v starts at b_it and go to e_it - 1 inserts v before w_it inserts n copies of v before w_it inserts b_it to e_it before w_it erases the element at w_it erases b_it to e_it
32
STL: Containers
int main() { map<string, int, less<string> > name_age; name_age["Pohl,Laura"] = 7; name_age["Dolsberry,Betty"] = 39; name_age["Pohl,Tanya"] = 14; cout << "Laura is " << name_age["Pohl,Laura"] << " years old." << endl; } In the above example, the map name_age is an associative array where the key is a string type. The Compare object is less<string>. Associative classes will be designated as ASSOC in the following description of their interface. Keep in mind that these are in addition to the already described CAN interface. STL Associative Definitions
ASSOC::key_type ASSOC::key_compare ASSOC::value_compare the retrieval key type the comparison object type the type for comparing ASSOC::value_type
The associative containers have several standard constructors for initialization. STL Associative Constructors
ASSOC() ASSOC(cmp) ASSOC(b_it, e_it) ASSOC(b_it, e_it, cmp) default constructor using Compare constructor using cmp as the comparison object uses element range b_it to e_it using Compare uses element range b_it to e_it and cmp as the comparison object
What distinguishes associative constructors from sequence container constructors is the use of a comparison object.
6.1
Containers
33
The insertion works when no element of the same key is already present. STL Member Functions
c.find(k) c.count(k) c.lower_bound(k) c.upper_bound(k) c.equal_range(k) returns iterator to element having the given key k, otherwise ends returns the number of elements with k returns iterator to first element having value greater than or equal to k returns iterator to first element having value greater than k returns an iterator pair for lower_bound and upper_bound
The associative containers are set, map, multiset, and multimap. They have keybased accessible elements. These containers have an ordering relation, Compare, which is the comparison object for the associative container. As a further associate container example we will use a multiset to count the number of times each vegetable enters our diet in the course of 100 meals.
34
STL: Containers
This program generates into vector a random diet of vegetables. it then uses the special properties of multiset to perform a count on how often each vegetable is eaten in our diet.
6.1
Containers
35
The queue can be adapted from list or deque . It needs an implementation that supports empty, size, front, back, push_back and pop_front operations. This is a first-in, first-out data structure. STL Adapted queue Functions
void push(const value_type& v) places v on the end of the queue void pop() value_type& front() const value_type& back() const bool empty() const size_type size() const operator== and operator< removes the front element of the queue returns the front element of the queue returns the back element of the queue returns true if the queue is empty returns the number of elements in the queue equality and lexicographically less than
The priority_queue can be adapted from vector or deque. It needs an implementation that supports empty, size, front, push_back, and pop_back operations. A priority_queue also needs a comparison object for its instantiation. The top element is the largest element as defined by the comparison relationship for the priority_queue. STL Adapted priority_queue Functions
void push(const value_type& v) void pop() value_type& top() const bool empty() const size_type size() const places v in the priority_queue removes top element of the priority_queue returns top element of the priority_queue checks for priority_queue empty shows number of elements in the priority_queue
36
STL: Containers
R X
6.1
Containers
37
Prescription Discussion
The vector is generally the easiest container to use. It is a simple generalization of the array and as such is most familar to programmers. It is also often the most efficient over a large class of operations. It should be your default container choice. The deque is the next most useful. Its ability to add to both ends of the data structure in constant time is its greatest strength .It is also supports random access access. The list in many ways is the most expensive container class. Its chief benefit is to give you insertion and deletion of internal elements in constant time without destroying existing iterator values. Again be guided by the most frequent operations required by your problem in making these choices. There is relative ease in switching among container. One container can be constructed by passing an iterator range from another container. Do not be afraid of using multiple representations for some problems that dictate a combination of space-operation cost tradeoffs. The point of STL is to use a most efficient algorithm. Usually this involves selecting the appropriate container. Container adaption results in a supported interface, such as a stack or priority queue that hides the underlying container implementation. Nevertheless the different implementations dictate the efficiency of the resulting data structure. Your choice should be sensative to what operations and space constraints are important to your problem. When in doubt profile your program.
38
STL: Iterators
Chapter 7
STL: Iterators
Navigation over containers is by iterator. Iterators can be thought of as an enhanced pointer type. They are templates that are instantiated as to the container class type they iterate over. There are five iterator types: input, output, forward, bidirectional, and random access (see Section 7.0.1, Iterator Categories, on page 39). Not all iterator types may be available for a given container class. For example, random access iterators are available for vectors but not maps. The input and output iterators have the fewest requirements. They can be used for input and output and have special implementations called istream_iterator and ostream_iterator for these purposes. (See Section 7.0.2, Istream_iterator, on page 40, and Section 7.0.3, Ostream_iterator, on page 41.) A forward iterator can do everything an input and output iterator can do and can additionally save a position within a container. A bidirectional iterator can go both forward and backward. A random access iterator is the most powerful and can access any element in a suitable container, such as a vector in constant time.
STL: Iterators
39
int main() { int primes[4] ={ 2, 3, 5, 7 }, *ptr = primes; set<int, greater<int> > s; set<int, greater<int> > :: const_iterator const_s_it; while (ptr != primes + 4 ) s.insert(*ptr++); cout << "The primes below 10 : " << endl; for (const_s_it = s.begin(); const_s_it != s.end(); ++const_s_it) cout << *const_s_it << '\t'; } The above program uses an iterator for a set container to output one-digit primes. Such an iterator needs to have the ability to autoincrement and to be dereferenced.
40
STL: Iterators
7.0.2 Istream_iterator
An istream_iterator is derived from an input_iterator to work specifically with reading from streams. The template for istream_iterator is instantiated with a <type, distance>. This distance is usually specified by ptrdiff_t. As defined in cstddef or stddef.h , it is an integer type representing the difference between two pointer values.
STL: Iterators
41
reads and caches a next value of type int from the designated input stream. The ostream_iterator out is constructed with the output stream cout and the char* delimiter \t. Thus the tab character will be issued to the stream cout after each int value is written. In this program the iterator out, when it is dereferenced, writes the assigned int value to cout.
7.0.3 Ostream_iterator
An ostream_iterator is derived from an output_iterator to work specifically with writing to streams.
ostream_iterator Program
In file stl_oitr.cpp //Use of as ostream_iterator iterator #include <iostream> #include <iterator> using namespace std; int main() { int d[5] = { 2, 3, 5, 7, 11 }; ostream_iterator<int> out(cout, "\t"); for (int i = 0; i < 5; ++i) *out = d[i] ; } The ostream_iterator can be constructed with a char* delimiter, in this case \t. Thus the tab character will be issued to the stream cout after each int value is written. In this program the iterator out , when it is dereferenced, writes the assigned int value to cout. The output stream iterator is isomorphic to the input stream iterator. When a value is assigned to the iterator, it is written to the instantiated output stream, using operator >>. As seen in the above example the output stream iterator must specify as a parameter to the constructor, the associated output stream. An optional second parameter to the constructor is a string that will be used as a separator between values. Simple file manipulations can be coded by using input and output stream iterators and various algorithms in the standard library. The following example reads a
//primes
42
STL: Iterators
file of integers, removes all occurrences of the value 0, and copies the remaining values separating each value with a comma:
istream_iterator Program
In file stl_ioitr.cpp //Use of both istream_iterator and ostream_iterator iterator #include <iostream> #include <iterator> using namespace std; void main() { istream_iterator<int, ptrdiff_t> input (cin), eof; ostream_iterator<int> output (cout, ","); //remove 0 from file redirected to cin //print file to cout remove_copy (input, eof, output, 0); }
STL: Iterators
43
44
STL: Iterators
template<class RandAccIter, class T, class Ref = T&, class Distance = ptrdiff_t> class reverse_iterator; This reverses the normal direction of iteration. Use rbegin() and rend() for range. template <class Can> class insert_iterator; template <class Can, class Iter> insert_iterator<Can> inserter(Can& c, Iter p); Insert iterator inserts instead of overwrites. The insertion into c is at position p. template <class Can> class front_insert_iterator; template <class Can> front_insert_iterator<Can> front_inserter(Can& c); Fr o n t i n s e r t i o n oc c u r s a t t h e f r o n t o f t h e c o n t a in e r a n d r e q u ir e s a push_front() member. template <class Can> class back_insert_iterator; template <class Can> back_insert_iterator<Can> back_inserter(Can& c); Back insertion occurs at the back of the container and requires a push_back() member. Check your vendors product for specific system-dependent implementations.
R X
STL: Iterators
45
Prescription Discussion
Iterator sequences are not tied to a particular type of container. Container types are a narrower style of representation than iterator ranges. Ergo using iterator sequences leaves algorithms more general and hence the more reusable. Our modus operandi in generic programming is to make the program as general as possible without degrading efficiency. This leads to rule two, namely use the weakest iterator type compatible with an efficient implementation of a computation.
46
STL: Algorithms
Chapter 8
STL: Algorithms
The STL algorithms library contains the following four categories. STL Categories of Algorithms Library Sorting algorithms Nonmutating sequence algorithms Mutating sequence algorithms Numerical algorithms These algorithms generally use iterators to access containers instantiated on a given type. The resulting code can be competitive in efficiency with special-purpose codes.
8 int main() { int d[N], i, *e = d + N; for (i = 0; i < N; ++i) d[i] = rand(); sort(d, e); for (i = 0; i < N; ++i) cout << d[i] << '\t'; }
STL: Algorithms
47
This is a straightforward use of the library sort algorithm operating on the built-in array d[]. Notice how ordinary pointer values can be used as iterators. We present the library prototypes for sorting algorithms. template<class RandAcc> void sort(RandAcc b, RandAcc e); This is a quicksort algorithm over the elements in the range b to e. The iterator type RandAcc must be a random access iterator. template<class RandAcc> void stable_sort(RandAcc b, RandAcc e); This is a stable sorting algorithm over the elements in the range b to e. In a stable sort equal elements remain in their relative same position. template<class RandAcc> void partial_sort(RandAcc b, RandAcc m, RandAcc e); This is a partial sorting algorithm over the elements in the range b to e. The range b to m is filled with elements sorted up to position m. template<class InputIter, class RandAcc> void partial_sort_copy(InputIter b, InputIter e, RandAcc result_b, RandAcc result_e); This is a partial sorting algorithm over the elements in the range b to e . Elements sorted are taken from the input iterator range and copied to the random access iterator range. The smaller of the two ranges is used.
48
STL: Algorithms
template<class RandAcc> void nth_element(RandAcc b, RandAcc nth, RandAcc e); The nth element is placed in sorted order, with the rest of the elements partitioned by it. For example, if the fifth position is chosen, the four smallest elements are placed to the left of it. The remaining elements are placed to the right of it and will be greater than it. template<class InputIter1, class InputIter2, class OutputIter> OutputIter merge(InputIter1 b1, InputIter1 e1, InputIter2 b2, InputIter2 e2, OutputIter result_b); The elements in the range b1 to e1, and b2 to e2 are merged to the starting position result_b. template<class BidiIter> void inplace_merge(BidiIter b, BidiIter m, BidiIter e); The elements in the range b to m and m to e are merged in place. We will use a table to briefly list other algorithms and their purpose as found in this library. STL Sort Related Library Functions
binary_search(b, e, t) lower_bound(b, e, t) upper_bound(b, e, t) true if t is found in b to e the first position for placing t while maintaining sorted order the last position for placing t while maintaining sorted order returns an iterator pair for the range where t can be placed maintaining sorted order places the locations e element into an already existing heap swaps the locations e element with the b locations element and reheaps performs a sort on the heap creates a heap produces the next permutation produces the previous permutation
equal_range(b, e, t)
STL: Algorithms
49
These algorithms have a form that uses a Compare object replacing operator<(), for example: template<class RandAcc, class Compare> void sort(RandAcc b, RandAcc e, Compare comp); This is a quicksort algorithm over the elements in the range b to e using comp for ordering.
50
STL: Algorithms
//mop
//hope
STL: Algorithms
51
We will use a table to briefly list other algorithms and their purpose as found in this library. STL Nonmutating Sequence Library Functions
next_permutation(b, e) prev_permutation(b, e) count(b, e, t, n) count_if(b, e, p, n) adjacent_find(b, e) produces next permutation produces previous permutation returns to n the count of elements equal to t returns to n the count of elements that make predicate p true returns the first position of adjacent elements that are equal; otherwise returns e returns the first position of adjacent elements satisfying the binary predicate binp; otherwise returns e returns an iterator pair indicating the positions where elements do not match from the given sequences starting with b1 and b2 as above, with a binary predicate binp used instead of equality returns true if the indicated sequences match; otherwise returns false as above, with a binary predicate binp used instead of equality returns an iterator where the second sequence is contained in the first, if it is not e1 as above, with a binary predicate binp used instead of equality
adjacent_find(b, e, binp)
mismatch (b1, e1, b2, binp) equal(b1, e1, b2) equal(b1, e1, b2, binp) search(b1, e1, b2, e2) search (b1, e1, b2, e2, binp)
52
STL: Algorithms
STL: Algorithms
53
template<class BidiIter1, class BidiIter2> BidiIter2 copy_backward(BidiIter1 b1, BidiIter1 e1, BidiIter2 b2); This is a copying algorithm over the elements b1 to e1. The copy is placed starting at b2. The copying runs backward from e1 into b2 , which are also going backward. The position returned is b2 - (e1 - b1). template<class BidiIter> void reverse(BidiIter b, BidiIter e); This reverses in place the elements b to e. template<class BidiIter, class OutputIter> OutputIter reverse_copy(BidiIter b1, BidiIter e1, OutputIter b2); This is a reverse copying algorithm over the elements b1 to e1 . The copy in reverse is placed starting at b2 . The copying runs backward from e1 into b2, which are also going backward. The position returned is b2 + (e1 - b1). template<class ForwIter> ForwardIter unique(ForwIter b, ForwIter e); The adjacent elements in the range b to e are erased. The position returned is the end of the resulting range. template<class ForwIter, class BinaryPred> ForwardIter unique(ForwIter b, ForwIter e, BinaryPred bp); The adjacent elements in the range b to e with binary predicate bp satisfied are erased. The position returned is the end of the resulting range. template<class InputIter, class OutputIter> OutputIter unique_copy(InputIter b1, InputIter e1, OutputIter b2); template<class InputIter, class OutputIter, class BinaryPred> OutputIter unique_copy(InputIter b1, InputIter e1, OutputIter b2, BinaryPred bp); The results are copied to b2 with the original range unchanged. The remaining library functions are described in the following tables.
54
STL: Algorithms
replace(b, e, t1, t2) replace_if(b, e, p, t2) replace_copy(b1, e1, b2, t1, t2)
copies and replace into b2 the range b1 replace_copy_if(b1, e1, b2, p, t2) to e1 with the elements satisfying the predicate p replacing t2 remove(b, e, t) remove_if, remove_copy, remove_copy_if fill(b, e, t) fill_n(b, n, t) generate(b, e, gen) generate_n(b, n, gen) removes elements of value t similar to replace family except that values are removed assigns t to the range b to e assigns n ts starting at b assigns to the range b to e by calling generator gen assigns n values starting at b using gen
STL: Algorithms
55
rotate(b, m, e)
partition(b, e, p)
stable_partition(b, e, p)
56
STL: Algorithms
int main() { double v1[3] = { 1.0, 2.5, 4.6 }, v2[3] = { 1.0, 2.0, -3.5 }; double sum, inner_p; sum = accumulate(v1, v1 + 3, 0.0); inner_p = inner_product(v1, v1 + 3, v2, 0.0); cout << "sum = " << sum << ", product = " << inner_p << endl; } These functions behave as expected on numerical types where + and * are defined. The library prototypes for numerical algorithms are as follows. template<class InputIter, class T> T accumulate(InputIter b, InputIter e, T t); This is a standard accumulation algorithm whose sum is initially t. The successive elements from the range b to e are added to this sum. template<class InputIter, class T, class BinOp> T accumulate(InputIter b, InputIter e, T t, BinOp bop); This is an accumulation algorithm whose sum is initially t. The successive elements from the range b to e are summed with sum = bop(sum, element). We will use a table to briefly list other algorithms and their purpose as found in this library.
STL: Algorithms
57
inner_product(b1,e1,b2,t,bop1,bop2)
partial_sum(b1, e1, b2) partial_sum(b1, e1, b2, bop) adjacent_difference(b1, e1, b2) adjacent_difference(b1, e1, b2, bop)
STL provides the basic computations for many more sophisticated algorithms. By using STL, programmers can easily implement them. We will use numerical integration as an example. The idea is to generate a series of points, using a generator. A generator is a class that defines the function by overloading operator(), the function call operator. The STL algorithm generate(iterator b, iterator e, generator g) is used to produce a vector of values in the range (0, 1) for the function. The algorithm, numeric, and vector libraries are all required.
58
STL: Algorithms
generate(fx.begin(),fx.end(), g ); return(accumulate(fx.begin(), fx.end(), 0.0) / n ); } int main() { const int n = 10000; gen g(0.0, 1.0/n); cout << "integration program x**2" << endl; cout << integrate(g, n) << endl; } We approximate the area under the curve by a sequence of rectangles whose height is the value of the function and whose width is the increment. An increment gives us two choices for a height. We could improve the numerical accuracy of integration by bounding the area between rectangles based on the smaller heights and one based on the larger heights.
STL: Algorithms
59
Integration Function
In file stl_int2.cpp double integrate( gen g, int n, double& diff) { vector<double> fx(n), sm(n), lg(n); double s, l; generate(fx.begin(),fx.end(), g ); for (int i = 0; i < n - 1; ++i) if (fx[i] > fx[i + 1]) { sm[i] = fx[i + 1]; lg[i] = fx[i]; } else { sm[i] = fx[i]; lg[i] = fx[i + 1]; } s = accumulate(sm.begin(), sm.end(), 0.0)/n ; l = accumulate(lg.begin(), lg.end(), 0.0)/n ; diff = l - s; return ( s + l ) / 2; } The preceding code produces a more reliable estimate, with an error estimate calculated in diff.
R X
Prescription Discussion
The STL algorithms are expected to be efficient. The generalized sort is an efficient adaptation of quicksort and compares favorably in most cases to running qsort() as found in the C standard library. As in the above example of numerical integration, STL routines can be readily employed and adapted to perform significant computations without resort to special codes. The use of various function adaptors as discussed in the next chapter will vastlyexpand the applicability of STL.
60
Chapter 9
STL: Function Objects
It is useful to have function objects to further leverage the STL library. For example, many of the previous numerical functions had a built-in meaning using + or *, but also had a form in which user-provided binary operators could be passed in as arguments. Defined function objects can be found in function or built. Function objects are classes that have operator() defined. These are inlined and are compiled to produce efficient object code.
9 STL Defined Function Object Classes Arithmetic objects Comparison objects Logical objects
61
We will use tables to briefly list algorithms and their purpose as found in this library. STL Arithmetic Objects
template <class T> struct plus<T> template <class T> struct minus<T> template <class T> struct times<T> template <class T> struct divides<T> template <class T> struct modulus<T> template <class T> struct negate<T> adds two operands of type T subtracts two operands of type T multiplies two operands of type T divides two operands of type T modulus for two operands of type T unary minus for one argument of type T
Arithmetic objects are often used in numerical algorithms, such as accumulate(). STL Comparison Objects
template <class T> struct equal_to<T> template <class T> struct not_equal_to<T> template <class T> struct greater<T> template <class T> struct less<T> template <class T> struct greater_equal<T> template <class T> struct less_equal<T> equality of two operands of type T inequality of two operands of type T comparison by the greater (>) of two operands of type T comparison by the less (<) of two operands of type T comparison by the greater or equal (>=) of two operands of type T comparison by the lesser or equal (<=) of two operands of type T
The comparison objects are frequently used with sorting algorithms, such as merge().
62
63
template <class ForwIter> void print(ForwIter first, ForwIter last, const char* title) { cout << title << endl; while (first != last) cout << *first++ << '\t'; cout << endl; } int main() { int data[3] = { 9, 10, 11 }; print(data, data + 3, "Original values"); transform(data, data + 3, data, bind2nd(times<int>(), 2)); print(data, data + 3, "New values"); } We will use a table to briefly list algorithms and their purpose as found in this library. STL Function Adaptors
template<class Pred> unary_negate<Pred> not1(const Pred& p) template<class Pred> binary_negate<Pred> not2(const Pred& p) template<class Op, class T> binder1st<Op>bind1st (const Op& op,const T& t) template<class Op, class T> binder2nd<Op>bind2nd (const Op& op,const T& t) template<class Arg,class T> ptr_fun(T (*f)(Arg)) template<class Arg1, class Arg2, class T> ptr_fun(T (*f)(Arg1, Arg2)) returns !p where p is a unary predicate
the binary op has a first argument bound to t; a function object is returned the binary op has a second argument bound to t; a function object is returned constructs a pointer_to_unary_function<Arg, T> constructs a pointer_to_binary_function<Arg,T>
64
9.1 Allocators
Allocator objects manage memory for containers. They allow implementations to be tailored to local system conditions while maintaining a portable interface for the container class. Allocator definitions include: value_type, reference, size_type, pointer, and difference_type. We will use a table to briefly list allocator member functions and their purpose as found in this library. STL Allocator Members
allocator(); ~allocator(); pointer address(reference r); pointer allocate(size_type n); void deallocate(pointer p); constructor and destructor for allocators returns the address of r allocates memory for n objects of size_type from free store deallocates memory associated with p returns the largest value for difference_type; in effect, the largest number of element allocatable to a container
size_type max_size();
R X
Prescription Discussion
In many cases a lack of understanding of the mathematical concept of function composition prevents a programmer from fully mastering the notion and techniques of adaptation. Many of these concepts are routinely used in functional languages or logic-based languages such as Lisp, ML, Scheme and Prolog. It can be useful to look at examples written in those languages to better understand how these ideas can apply to STL.
10
String Library
65
Chapter 10
String Library
C++ provides a string type by including the standard header file string . It is the instantiation of a template class basic_string<T> with char. The string type provides member functions and operators that perform string manipulations, such as concatenation, assignment, or replacement. An example of a program using the string type for simple string manipulation follows.
66
10
String Library
while (pos < sentence.size()) { pos = sentence.find(' ', old_pos); words[i++].assign(sentence, old_pos, pos - old_pos); cout << words[i - 1] << endl; //print words old_pos = pos + 1; } nwords = i; sentence = "C++ programmers "; for (i = 1; i < nwords -1; ++i) sentence += words[i] + ' '; sentence += "windows"; cout << sentence << endl; } The string type is used to capture each word from an initial sentence where the words are separated by the space character. The position of the space characters is computed by the find() member function. Then the assign() member function is used to select a substring from sentence. Finally, a new sentence is constructed using the overloaded assignment, operator+=(), and operator+() functions to perform assignments and concatenations. We will describe the representation for a string of characters. It is also usual to have the instantiation basic_string<wchar_t> for a wide string type wstring. Other instantiations are possible as well. String Private Data Members
char* ptr size_t len size_t res for pointing at the initial character for the length of the string for the currently allocated size, or for an unallocated string its maximum size
This implementation provides an explicit variable to track the string length, thus string length can be looked up in constant time, which is efficient for many string computations.
10.1
Constructors
67
10.1 Constructors
Strings have six public constructors, which makes it easy to declare and initialize strings from a wide range of parameters. String Constructor Members
string() string(const char* p) string(InputIterator b, InputIterator e) string(const string& str, size_t pos = 0, size_t n = npos) string(const char* p, size_t n) string(size_t n, char c1) default, creates an empty string. conversion constructor from a pointer to char constructor from the InputIterator range from b to e copy constructor; npos is usually -1 and indicates no memory was allocated copy n characters where p is the base address construct a string of n cs
These constructors make it quite easy to use the string type initialized from char* pointers, which was the traditional C method for working with strings. Also, many computations are readily handled as a vector of characters. This is also facilitated by the string interface.
68
10
String Library
There is an extensive set of public member functions that let you manipulate strings. In many cases these are overloaded to work with string, char*, and char. We will start by describing append(). string& append(const string& s, size_t pos = 0, size_t n=npos); Appends n characters starting at pos from s to the implicit string object. //example s1 "I am " s2 "7 years old" s1.append(s2); // s1 " I am 7 years old" s2.append(s1,0,4); //s2 "7 years old I am" string& append(const char* p, size_t n); string& append(const char* p); string& append(size_t n, char c); In each case a string object is constructed using the constructor of the same signature and appended to the implicit string object. string& assign(const string& s, size_t pos = 0, size_t n=npos); Assigns n characters starting at pos from s to the implicit string object.
10.2
Member Functions
69
//example s1 " I am " s2 "7 years old" s1.assign(s2); // s1 "7 years old" The following signatures with the expected semantics are also overloaded: string& string& string& string& assign(const char* p, size_t n); assign(const char* p); assign(size_t n, char c); assign(InputIterator b, InputIterator e);
string& insert(size_t pos1, const string& str, size_t pos2 = 0, size_t n = npos); The insert() function is an overloaded set of definitions that insert a string of characters at a specified position. It inserts n characters taken from str, starting with pos2, into the implicit string at position pos1. //example s1 " I am " s2 " 7 years old" s1.insert(2,s2); // s1"I 7 years old am" The following signatures with the expected semantics are also overloaded: string& insert(size_t pos,const char* p, size_t n); string& insert(size_t pos, const char* p); sstring& insert(size_t pos, size_t n, char c); iterator insert(iterator p, char c); iterator insert(iterator p, size_t n, char c); void insert(iterator p, InputIterator b, InputIterator e); The inverse function is remove(). string& remove(size_t pos = 0, size_t n = npos); n characters are removed from the implicit string at position pos. In the following table, we briefly describe further public string member functions.
70
10
String Library
String Members
string& replace(pos1, n1, str, pos2 = 0, n2 = npos) string& replace(pos,n,p,n2); string& replace(pos,n,p); string& replace(pos,n,c); size_t length() const; const char* c_str() const; const char* data() const; replaces at pos1 for n1 characters, the substring in str at pos2 of n2 characters replaces n characters at pos, using a char* p of n2 characters, or a char* p until null, or a character c returns the string length converts string to traditional char* representation returns base address of the string representation resizes the string to length n; the padding character c is used in the first function and the eos() character is used in the second allocates memory for string; returns the size of the allocation the implicit string starting at pos is copied into the char* p for n characters a substring of n characters of the implicit string is returned
void resize(n, c); void resize(n); void reserve(size_t res_arg); size_t reserve() const; size_t copy(p, n, pos=0) const;
You can lexicographically compare two strings using a family of overloaded member functions compare(). int compare(const string& str, size_t pos = 0, size_t n = npos) const; Compares the implicit string starting at pos for n characters with str. Returns zero if the strings are equal; otherwise returns a positive or negative integer value indicating that the implicit string is greater or less than str lexicographically. The following signatures with the expected semantics are also overloaded: int compare(const char* p,size_t pos, size_t n) const; int compare(const char* p, size_t pos = 0) const;
10.2
Member Functions
71
Each signature specifies how the explicit string is constructed and then compared to the implicit string. The final set of member functions perform a find operation. We will discuss one group and then summarize in a table the rest of this group of member functions. size_t find(const string& str, size_t pos=0) const; The string str is searched for in the implicit string starting at pos. If it is found the position it is found at is returned; otherwise npos is returned, indicating failure. The following signatures with the expected semantics are also overloaded: size_t find(const char* p, size_t pos, size_t n)const; size_t find(const char* p, size_t pos= 0) const; size_t find(char c, size_t pos = 0) const; Each signature specifies how the explicit string is constructed and then searched for in the implicit string. Further functions for finding strings and characters are briefly described in the following table. String Find Members
size_t size_t size_t size_t rfind(str, pos=npos) const; like find(), but scans the string rfind(p, pos, n) const; backward for a first match rfind(p, pos=npos) const; rfind(c, pos=npos) const;
size_t find_first_of (str, pos = 0) const; size_t find_first_of (p, pos, n) const; size_t find_first_of (p, pos=0) const; size_t find_first_of (c,pos = 0) const;
searches for the first character of any character in the specified pattern, either str, char* p, or char c
72
10
String Library
searches backward for the first character of any character in the specified pattern, either str, char* p, or char c
searches for the first character that does not match any character in the specified pattern, either str, char* p, or char c
searches backward for the first character that does not match any character in the specified pattern, either str, char* p, or char c
10.3
Global Operators
73
The comparison operators and the concatenation operator+() are also overloaded with the following four signatures: bool bool bool bool operator==(const char* p, const string& s); operator==(char c, const string& s); operator==(const string& s, const char* p); operator==(const string& s, char c);
In effect, a comparison or concatenation of any kind can occur between string and a second argument that is either a string, a character, or a character pointer.
R X
74
11
References
Chapter 11
References
ABC 95 Kelley, A., and Pohl, I., A Book on C, Third Edition. 1995. Reading, MA: Addison-Wesley. ARM 90 Ellis, M., and Stroustrup, B., The Annotated C++ Reference Manual . 1990. Reading, MA: Addison-Wesley. C4C 94 Pohl, I., C++ for C Programmers, Second Edition. 1994. Reading, MA: AddisonWesley. C4P 95 Pohl, I., C++ for Pascal Programmers, Second Edition. 1995. Reading, MA: Addison-Wesley. DE 94 Stroustrup, B., The Design and Evolution of C++. 1994. Reading, MA: Addison-Wesley. DP 95 Gamma, E., Helm, R., Johnson, R., and Vlissides, J., Design Patterns: Elements of Reusable Object-Oriented Software. 1995. Reading, MA: Addison-Wesley. EC 92 Meyers, S., Effective C++: 50 Specific Ways to Improve your Programs and Designs. 1992. Reading, MA: Addison-Wesley. GRAY 91 Stroustrup, B., The C++ Programming Language, Second Edition. 1991. Reading, MA: Addison-Wesley. IOS 93 Teale, S., C++ IO Streams Handbook. 1993. Reading, MA: Addison-Wesley. K 97 Knuth, Donald E., The Art of Computer Programming: Volume 1 Fundamental Algorithms: 3rd edition. 1997. Reading, MA: Addison-Wesley
11
References
75
K 98 Knuth, Donald E., The Art of Computer Programming: Volume 3 Sorting and Searching: 2nd edition. 1998. Reading, MA: Addison-Wesley KR 88 Kernighan, B., and Ritchie, D., The C Programming Language, Second Edition. 1988. Englewood Cliffs, NJ: Prentice Hall. KP 74 Kernighan, B., and Plauger, P., The Elements of Programming Style. 1974. New York, NY: McGraw-Hill. LIP 91 Lippman, S., The C++ Primer, Second Edition . 1991. Reading, MA: AddisonWesley. OOAD 94 Booch, G., Object-Oriented Analysis and Design, Second Edition. 1995. Reading, MA: Addison-Wesley. OPUS 97 Pohl, I., Object-Oriented Programming Using C++, Second Edition. 1997. Reading, MA: Addison-Wesley.OPUS 97 P 72 Pohl, I., 1972. "A Sorting Problem and Its Complexity," CACM, v. 15, no. 6, June 1972, pp. 462-464. P 97 Pohl, I., C++ Distilled. 1997. Reading, MA: Addison-Wesley. P 99 Pohl, I., C++ for C Programmers. 1999. Reading, MA: Addison-Wesley. S 97 Stroustrup, B., The C++ Programming Language, 3rd Edition. 1997. Reading, MA: Addison-Wesley STL 96 Musser, D. and Saini, A., STL Tutorial and Reference Guide: C++ Programming with the Standard Template Library. 1996. Reading, MA: Addison-Wesley. STLP 96 Glass, G. and Schuchert, B., The STL <Primer>. 1996. Upper Saddle River, NJ: Prentice Hall. TG 94 Taligent Inc., Taligents Guide to Designing Programs: Well-Mannered ObjectOrietned Design in C++. 1994. Reading, MA: Addison-Wesley.
76
12
Supplemental Programs
Chapter 12
Supplemental Programs
We will use this section to give added examples of programs that use ideas in this book. To the extent that this section and the eMatter book prompt further reader queries material will be added to this and other sections.
Generic Copy
In file copy2.cpp /*Filename: copy2.cpp Supplement: Generic Programming and STL Compiler: Borland C++ Version 5.01 Copyright By Ira Pohl */ #include <iostream> #include <assert> //using namespace std; template<class T1, class T2> void copy(T1 a[], T2 b[], int n) { for (int i = 0; i < n; ++i) a[i] = b[i]; }
12.1
77
template<class TYPE> void print(TYPE a[], int n) { cout << "\nNEW PRINT ="; for (int i = 0; i < n; ++i) cout << a[i] << " "; } int main() { double char int char* int i;
for (i = 0; i < 25; ++i) c1[i] = 'a' + i/8; for (i = 0; i < 75; ++i) { i1[i] = 2 * i; i2[i] = i * i; } print(f1, print(f2, print(i1, print(i2, print(c1, print(c2, 20); 20); 20); 20); 20); 20); //print initial values
78
12
Supplemental Programs
copy(f1, f2, 50); copy(c1, c2, 10); copy(i1, i2, 40); copy(ptr1, ptr2, 100); // copy(i1, f2, 50); // copy(ptr1, f2, 50); print(f1, print(f2, print(i1, print(i2, print(c1, print(c2, } 20); 20); 20); 20); 20); 20);
12.2
Generic Stack
79
//template stack implementation template <class TYPE> class stack { public: explicit stack(int size = 100) : max_len(size), top(EMPTY),s(new TYPE[size]) { assert(s != 0); } ~stack() { delete []s; } void reset() { top = EMPTY; } void push(TYPE c) { s[++top] = c; } TYPE pop() { return s[top--]; } TYPE top_of()const { return s[top]; } bool empty()const { return top == EMPTY;} bool full()const { return top == max_len - 1;} private: enum { EMPTY = -1 }; TYPE* s; int max_len; int top; }; //Reversing an array of char* represented strings void reverse(char* str[], int n) { stack<char*> stk(n); int i; for (i = 0; i < n; ++i) stk.push(str[i]); for (i = 0; i < n; ++i) str[i] = stk.pop(); } template <class T1> void print_and_pop(T1& a, char* comment) { cout << Printing << comment << endl; while (!a.empty()) cout << a.pop() << \t; cout << endl; }
80
12
Supplemental Programs
//Initializing stack of complex numbers from an array int main() { stack<char> stk_ch; // 1000 char stack stack<char*> stk_str(200); // 200 char* stack char* str[3] = {Reverse, these, three}; stk_ch.push(A); stk_ch.push(B); print_and_pop(stk_ch, char); stk_str.push(ABCD); stk_str.push(EFGH); print_and_pop(stk_str, char*); reverse(str, 2); cout << Reversed 2 << endl << str[0] << str[1] << str[2] << endl; }
12.3
Reverse Iterator
81
int main() { int data[3] = { 9, 10, 11}; vector<int> d(data, data + 3); vector<int>::reverse_iterator p = d.rbegin(); print(d.begin(), d.end(), "Original"); print(p, d.rend(), "Reverse"); }
82
12
Supplemental Programs
Index
Symbols
{} braces,
A
accumulate, 27, 55, 56 accumulate program, 5 address, 64 adjacent_difference, adjacent_find, 51 algorithm, 26 algorithm library, 46,
57
algorithms, 46 allocate, 64 allocator object, 64 append(), 68 argument template, 11 array program, 12 assign(), 68, 69 associative container, 27, 31
52, 57
50,
compare(), 70 comparison object, 31, 33, 35, 61 comparison operator, 23, 29 container, 5, 2627 adaptor, 34 class, 39 copy, 51, 52 copy program, 76 copy(), 70, 76 copy_backward, 53 count, 33, 51 count_if, 51 cstddef library, 40
D
data(), 70 deallocate, 64 declaration template, 11 deque, 2728, 30, 3435 deque library, 30
find_last_not_of(), 72 find_last_of(), 72 for_each, 50 forward iterator, 3839 friend, 15 front, 35 front_inserter, 44 function adaptor, 62 friend, 15 object, 60 overloading, 73 template, 13 function library, 60, 62 function object, 60 functions accumulate(), 27, 55, address(), 64 adjacent_difference (), 57 adjacent_find(), 51 allocate(), 64 append(), 68 assign(), 68, 69 back(), 35 back_inserter(), 44 begin(), 23, 27, 29 binary_search(), 48 bind1st(), 63 bind2nd(), 63 c_str(), 70 compare(), 70 copy(), 51, 52, 70, 76 copy_backward(), 53 count(), 33, 51 count_if(), 51 data(), 70 deallocate(), 64 empty(), 23, 29, 35 end(), 23, 27, 29
56
B
back, 35 back_inserter, 44 begin, 23, 27, 29 bidirectional iterator, 38 39 binary_search(), 48 bind1st, 63 bind2nd, 63 Booch, G., 75 braces {}, 4
E
Ellis, M., 74 empty, 23, 29, 35 end, 23, 27, 29 equal, 51 equal_range, 33, 48 equality operator, 23, 29 erase, 31, 33
F
fill, 54 find, 33, 49, 50 find(), 66, 71 find_first_not_of(), find_first_of(),
C
c_str(), 70 class, 12 compare object, 46
72
71
84
Index
equal(), 51 equal_range(), partial_sort(), 47 partial_sort_copy() partial_sum(), 57 partition(), 55 pop(), 35 pop_heap(), 48 prev_permutation(), print(), 26, 43, 63 ptr_fun(), 63 push(), 35 push_heap(), 48 random_shuffle(), 55 rbegin(), 23, 29 remove(), 54 remove_copy(), 54 remove_copy_if(), 54 remove_if(), 54 rend(), 23, 29 replace(), 54, 70 replace_copy(), 54 replace_copy_if(), replace_if(), 54 reserve(), 70 resize(), 70 reverse(), 51, 53 reverse_copy(), 53 rfind(), 71 rotate(), 55 rotate_copy(), 55 search(), 51 set_difference(), 49 set_intersection(), swap(), 13, 23, 29, 54 swap_range(), 54 top(), 35 transfer(), 1, 2 transform(), 54 unique(), 53 unique_copy(), 53 upper_bound(), 33,
33,
48
, 47
66, 71
, 72
find_first_not_of() find_first_of(), 71 find_last_not_of(), find_last_of(), 72 for_each(), 50 front(), 35 front_inserter(), 44 generate(), 54 generate_n(), 54 includes(), 49 inner_product(), 55, inplace_merge(), 48 insert(), 31, 33, 69 inserter(), 44 iter_swap(), 54 length(), 70 lexicographical_com pare(), 49 lower_bound(), 33, make_heap(), 48 max(), 49 max_element(), 49 max_size(), 23, 29, maxelement(), 16 merge(), 48 min(), 49 min_element(), 49 mismatch(), 51 next_permutation(), not1(), 63 not2(), 63 nth_element(), 48 operator+(), 66 operator+=(), 66
48, 51
48
G
Gamma, E, 74 generate, 54 generate_n, 54 generic, 1, 20 Glass, G., 75
72
H
hello program, Helm, R, 74
57
I
includes, 49 inner_product, 55, 57 inplace_merge(), 48 input iterator, 3839 insert, 31, 33 insert(), 69 inserter, 44 instantiation, 15 Istream_iterator, 40 iter_swap, 54 iterator, 26, 3839 iterator adaptor, 42 iterator library, 40, 41, 42
54
48
64
49
set_symmetric_diffe rence(), 49 set_union(), 49 size(), 23, 29, 35 sort(), 27, 46, 47, 49 sort_heap(), 48 stable_partition(), stable_sort(), substr(), 70 sum(), 28
J
Johnson, R, 74
48, 51
K
Kelley, A, 74 Kernighan, B., 4, 75 keywords class, 12 friend, 15
55
47
Index
signed char, 73 static, 15 template, 11 Knuth, D., 10, 74 map library, 31 max, 49 max_element, 49 max_size, 23, 29, 64 maxelement(), 16 merge(), 48 Meyers, S., 74 min, 49 min_element, 49 mismatch, 51 multimap, 27, 31, 33 multiset, 27, 31, 33 Musser, D., 75
85
L
length(), 70 lexicographical_compa re, 49 libraries algorithm, 46, 50, 52, cstddef, 40 deque, 30 function, 60, 62 iterator, 40, 41, 42 list, 26 map, 31 numeric, 26, 55, 57, set, 38 stack, 36 stddef, 40 vector, 30,
57
N
next_permutation,
pop_heap(), 48 prescriptions Algorithms, 10 Basics and the Container vector, 25 Containers and Iterators, 6 Function Objects, 64 General Rules, 3 Iterators, 44 STL Containers, 36 String Library, 73 Style and Rule, 8 Templates, 19 prev_permutation, 48, print, 63 print(), 26, 43 priority_queue, 3435 programs accumulate, 5 array, 12 copy, 76 hello, 8 stack, 11 stl_adap, 62 stl_age, 31, 34 stl_cont, 26 stl_deq, 28 stl_find, 50 stl_fucn, 60 stl_iadp, 43 stl_io, 40 stl_iter, 38 stl_numr, 55 stl_oitr, 41, 42 stl_revr, 52 stl_sort, 46 stl_stak, 36 stl_vect, 30 string, 65 swap, 13 template, 6 transferArray, 1, 2, 3 vector, 16, 21, 22, 24 ptr_fun, 63 push, 35
48,
51
60
Lippman, S., 75 list, 27, 30, 3435 list library, 26 lists Overloaded Function Selection Algorithm, 14 STL Categories of Algorithms Library, 46 STL Defined Function Object Classes, 61 STL Function Adaptors, 62 STL Iterator Adaptors, 42 STL Typical Container Interfaces, 28 lower_bound, 33 lower_bound(), 48
52, 57
nonmutating algorithm, 49 not1, 63 not2, 63 nth_element(), 48 numeric library, 26, 55, numerical algorithm, 55
51
57, 60
O
operator+(), 66 operator+=(), 66 ostream_iterator, 41 output iterator, 3839 overloading function, 73 template function, 16
P
parametric polymorphism, partial_sort(), 47 partial_sort_copy(), partial_sum, 57 partition, 55 Plauger, P., 75 Pohl, I., 7475 pop, 35
11 47
M
make_heap, 48 map, 27, 31, 33
86
Index
push_heap(),
48
Q
queue, 3435
R
random access iterator, 3839 random_shuffle, 55 ranges, 6 rbegin, 23, 29 references, 74 remove, 54 remove_copy, 54 remove_copy_if, 54 remove_if, 54 rend, 23, 29 replace, 54 replace(), 70 replace_copy, 54 replace_copy_if, 54 replace_if, 54 reserve(), 70 resize(), 70 reverse, 51, 53 reverse_copy, 53 rfind(), 71 Ritchie, D., 4, 75 rotate, 55 rotate_copy, 55
S
Saini, A., 75 Schuchert, B., 75 search, 51 sequence algorithm, 51 sequence container, 27, 30 set, 27, 31, 33 set library, 38 set_difference, 49 set_intersection, 49 set_symmetric_differe nce, 49 set_union, 49 signed char, 73 size, 23, 29, 35 sort, 27, 49
sort(), 46, 47 sort_heap, 48 sorting algorithm, 46 stable_partition, 55 stable_sort(), 47 stack, 34 stack library, 36 stack program, 11 static, 15 stddef library, 40 STL reverse_bidirectional_it erator, 43 reverse_iterator, 44 stl_adap program, 62 stl_age program, 31, 34 stl_cont program, 26 stl_deq program, 28 stl_find program, 50 stl_fucn program, 60 stl_iadp program, 43 stl_io program, 40 stl_iter program, 38 stl_numr program, 55 stl_oitr program, 41, 42 stl_revr program, 52 stl_sort program, 46 stl_stak program, 36 stl_vect program, 30 storage types static, 15 string, 65 constructor, 67 data member, 66 find member, 71 function member, 70 global operator, 73 member function, 68 overloaded operator, 68 string program, 65 Stroustrup, B., 4, 7475 style, 34 substr(), 70 sum(), 28 swap, 23, 29, 54 swap program, 13 swap(), 13
swap_range,
54
T
tables Container Operators, 24, 30 STL Adapted priority_queue Functions, 35 STL Adapted queue Functions, 35 STL Adpated stack Functions, 35 STL Allocator Members, 64 STL Arithmetic Objects, 61 STL Associative Constructors, 32 STL Associative Definitions, 32 STL Comparison Objects, 61 STL Container Definitions, 23, 29 STL Container Members, 23, 29 STL Function Adaptors, 63 STL Insert and Erase Member Functions, 33 STL Logical Objects, 62 STL Member Functions, 33 STL Mutating Sequence Library Functions, 54 STL Non-mutating Sequence Library Functions, 51 STL Numerical Library Functions, 57 STL Sequence Members, 31 STL Sort Related Library Functions, 48
Index
String Constructor Members, 67 String Find Members, 71 String Members, 70 String Overloaded Global Operators, 73 String Overloaded Operator Members, 68 String Private Data Members, 66 Taligent, 75 Teale, S., 74 template, 7, 1920 argument, 11 container, 19 declaration, 11 function, 13 specialization, 16 template, 11 template program, 6 top, 35 transfer(), 1, 2 transferArray program, 1, transform, 54 type safety, 20 string, 65 types class, 12 signed char, template, 11
87
2, 3
73
U
unique, 53 unique_copy, 53 upper_bound, 33 upper_bound(), 48
V
vector, 27, 30, 3435 vector library, 30, 36, vector program, Vlissedes, J., 74
40,
16, 21,