#include #include #include #include #include "vector.hpp" #include "output_iterator.hpp" namespace cs427_527 { template Iter find(const Iter& start, const Iter& end, const T& key); template Iter find_if(const Iter& start, const Iter& end, Pred f); // I don't think this is a good idea any more because of needing to // send "done" to the output iterator, which is not past of the // OutputIterator interface template void copy_with_terminator(const IIter& start, const IIter& end, const OIter& out); } // predicates for use with find/find_if void print(const double& i); bool startsWithJ(const std::string& name); // function objects for use with find/find_if class Summer { public: Summer(int& s) : sum(s) {} void operator()(const double& item) { sum += item; } private: int& sum; }; class StartsWith { public: StartsWith(char c) { ch = c; } bool operator()(const std::string& name) const { return name.length() > 0 && name[0] == ch; } private: char ch; }; // a slightly shorter name of a vector of ints using ivector427527 = cs427_527::Vector; /** * Prints the contents of the given vector of ints to standard output. */ void printVector(const ivector427527& v); /** * Prints the contents of the given view to standard output. */ void printView(const ivector427527::skip_view& v); /** * Prints the contents of the given containter by printing its views * to standard output. */ template void printViews(const Container& c); /** * Prints the contents of the given container/view to standard output. * * @param Container an iterable container or view type with printable elements * @param c the container to print */ template void printContainer(const Container& c); /** * Sets the element the given iterator is positioned at to the * given value. */ template void setViaIterator(const Iter& i, const T& value); int main(int argc, char **argv) { // create a vector containing 0, ..., 9 ivector427527 v1; for (int i = 0; i < 10; i++) { v1.push_back(i); } // create another such vector... ivector427527 v2; for (int i = 0; i < 9; i++) { v2.push_back(i); } // ...and then set each element to the square of what it used to be for (auto i = v2.begin(); i != v2.end(); ++i) { *i = *i * *i; } // print the vectors std::cout << "=== Two vectors ===" << std::endl; printVector(v1); printVector(v2); std::cout << "=== Evens and Odds ===" << std::endl; printView(v2.evens()); printView(v2.odds()); std::cout << "=== Evens and Odds through const & ===" << std::endl; printViews(v2); // we shouldn't need to write two separate functions for that -- template // functions to the rescue! std::cout << "=== Using template function ===" << std::endl; printContainer(v1); printContainer(v2); printContainer(v2.evens()); printContainer(v2.odds()); // initialize a STL vector using initializer list std::vector v = {1, 2, 3}; // note that the element a const iterator is at can be modified! // (const-ness for STL iterators refers to whether the iterators // can be moved from where they are; an iterator through a container // of const elements is a const_iterator) setViaIterator(v.begin(), 42); std::cout << "=== STL vector after setting through const iterator ===" << std::endl; printContainer(v); // ours work the same way as the STL's (but we haven't implemented // initialization via initializer lists; and note that unlike this // vector implementation, the Assignment #3 Matrix const iterators should // not allow modification of the element they are positioned at // (I have come to regret that decision) cs427_527::Vector w; w.push_back(1); w.push_back(2); w.push_back(3); std::cout << "=== Ours after setting through const iterator ===" << std::endl; setViaIterator(w.begin(), 42); printContainer(w); std::vector x; x.push_back(42.0); x.push_back(2.0); std::cout << "=== Comparing to vector of doubles ===" << std::endl; std::cout << (w == x) << std::endl; x.push_back(4.0); std::cout << (w == x) << std::endl; x[2] = 3.0; std::cout << (w == x) << std::endl; cs427_527::Vector names; names.push_back("Jim"); names.push_back("Karen"); // x == names; template instantiation fails on *i == *j (int == std::string) auto i = cs427_527::find(names.begin(), names.end(), "Karen"); if (i != names.end()) { std::cout << "found " << *i << std::endl; } auto j = cs427_527::find_if(names.begin(), names.end(), startsWithJ); if (j != names.end()) { std::cout << "found " << *j << std::endl; } // std::cout << "Enter a letter" << std::endl; // std::cin >> ch; char ch = 'K'; auto k = cs427_527::find_if(names.begin(), names.end(), StartsWith{ch}); if (k != names.end()) { std::cout << "found " << *k << std::endl; } // compute the sum using a function object (note that the function object // has a *reference* to an int as its data member so we can have it // change the local variable sum) int sum = 0; Summer s{sum}; std::for_each(x.begin(), x.end(), Summer{sum}); std::cout << "sum via function object: " << sum << std::endl; // equivalent to the above, but upon seeing the lambda expression, // the compiler automatically creates the function object for us // (including its class) sum = 0; std::for_each(x.begin(), x.end(), [&](const double& item) { sum += item; }); std::cout << "sum via lambda expression: " << sum << std::endl; cs427_527::copy_with_terminator(x.begin(), x.end(), OutputIterator{std::cout}); std::cout << std::endl; cs427_527::copy_with_terminator(x.begin(), x.end(), OutputIterator{std::cout, ",", "[", "]"}); std::cout << std::endl; } void print(const double& i) { std::cout << i << std::endl; } bool startsWithJ(const std::string& name) { return name.length() > 0 && name[0] == 'J'; } namespace cs427_527 { /** * Iter must be: copy-constructable, comparable with !=, * dereferencable to a type that is comparable to T with != * incrementable */ template Iter find(const Iter& start, const Iter& end, const T& key) { auto i = start; while (i != end && *i != key) { ++i; } return i; } template Iter find_if(const Iter& start, const Iter& end, Pred f) { auto i = start; while (i != end && !f(*i)) { ++i; } return i; } template void copy_with_terminator(const IIter& start, const IIter& end, const OIter& out) { IIter i = start; OIter j = out; while (i != end) { *j = *i; ++i; ++j; } j.terminate(); } } void printVector(const ivector427527& v) { std::cout << "["; for (auto i = v.begin(); i != v.end(); ++i) { std::cout << " " << *i; } std::cout << " ]" << std::endl; } void printView(const ivector427527::skip_view& view) { std::cout << "["; for (auto i = view.begin(); i != view.end(); ++i) { std::cout << " " << *i; } std::cout << " ]" << std::endl; } // Did you notice how the bodies of the above two functions were // exactly the same? How about we have the compiler write them for // us? template void printContainer(const Container& c) { std::cout << "["; for (auto i = c.begin(); i != c.end(); ++i) { std::cout << " " << *i; } std::cout << " ]" << std::endl; } template void printViews(const Container& c) { std::cout << "EVENS:"; printContainer(c.evens()); std::cout << "ODDS:"; printContainer(c.odds()); } template void setViaIterator(const Iter& i, const T& value) { // const means we can't move the iterator, not that we can't // change the element it is positioned at *i = value; }