Challange Lab- Sets

 Objective

C++ unordered_set overview

The C++ Standard Library contains an unordered_set class that implements a dynamic set. unordered_set supports range-based for loops to iterate through the set's items. Additional functionality is described in online documentation.

Step 1: Inspect StaticSet.h

Inspect the StaticSet template class declaration in the StaticSet.h file. Access StaticSet.h by clicking on the orange arrow next to main.cpp at the top of the coding window. StaticSet uses an unordered_set to implement a static set. The unordered_set's contents are assigned at construction time and must not change after.

Constructors and some additional member functions are already implemented:

  • Contains(const T& item) uses the unordered_set's count() function to determine if the set contains the item. If count() returns 1, the item is in the set and Contains() returns true. Otherwise the item is not in the set and Contains() returns false.

  • GetSize() uses the unordered_set's size() function to determine the number of elements in the set. GetSize() returns the set's size as an integer.

Step 2: Implement StaticSet's Union(), Intersection(), Difference(), Filter(), and Map() member functions

Implement the StaticSet class's Union(), Intersection(), Difference(), Filter(), and Map() member functions. Each must not change the StaticSet itself, but rather build and return a new StaticSet.

Step 3: Test in develop mode, then submit

File main.cpp contains test cases for each of the five operations. Running code in develop mode displays the test results, with 3 points possible for the Union(), Intersection(), and Difference() operations, and 2 points for the Filter() and Map() operations. After each member function is implemented and all tests pass in develop mode, submit the code. The unit tests run on submitted code are similar, but use different sets and template types.

 Code Starter

StaticSet.h

#ifndef STATICSET_H #define STATICSET_H #include <functional> #include <iostream> #include <unordered_set> #include <vector> template <typename T> class StaticSet { private: std::unordered_set<T> set; public: // Constructs an empty StaticSet StaticSet() { } // Constructs the StaticSet by adding all vector items StaticSet(const std::vector<T>& items) { for (const T& item : items) { set.insert(item); } } // Constructs the StaticSet by copying itemsToCopy's items StaticSet(const std::unordered_set<T>& itemsToCopy) : set(itemsToCopy) { } virtual ~StaticSet() { } bool Contains(const T& item) const { return 1 == set.count(item); } int GetSize() const { return (int) set.size(); } void Print(std::ostream& output, std::string separator, std::string prefix="", std::string suffix="") const { output << prefix; bool firstItem = true; for (const T& item : set) { if (firstItem) { output << item; firstItem = false; } else { output << separator << item; } } output << suffix; } // Returns a StaticSet<T> containing each element from this set that is not // in otherSet. StaticSet<T> Difference(const StaticSet& otherSet) const { // Your code here (remove placeholder line below) return StaticSet<T>(); } // Returns a StaticSet<T> containing each element from this set that // satisfies the predicate. // - If predicate(item) returns true, item satisfies the predicate. // - If predicate(item) returns false, item does not satisfy the predicate. StaticSet<T> Filter(std::function<bool(const T&)> predicate) const { // Your code here (remove placeholder line below) return StaticSet<T>(); } // Returns a StaticSet<T> containing each element from this set that is also // in otherSet. StaticSet<T> Intersection(const StaticSet& otherSet) const { // Your code here (remove placeholder line below) return StaticSet<T>(); } // Calls mapFunction(item) for each item in this set and adds the returned // item to a StaticSet<U>. After mapFunction has been called for each item // in this set, the StaticSet<U> object is returned. template <typename U> StaticSet<U> Map(std::function<U(const T&)> mapFunction) const { // Your code here (remove placeholder line below) return StaticSet<U>(); } // Returns a StaticSet<T> containing every element from this set and every // element from otherSet. StaticSet<T> Union(const StaticSet& otherSet) const { // Your code here (remove placeholder line below) return StaticSet<T>(); } }; #endif

StaticSetTestCases.h

#ifndef STATICSETTESTCASES_H #define STATICSETTESTCASES_H #include <iostream> #include <string> #include <tuple> #include <unordered_set> #include <vector> #include "StaticSet.h" class StaticSetTestCase { public: template <typename T> static double CompareSets(std::ostream& testFeedback, StaticSet<T>& actual, std::unordered_set<T>& expected, std::string title, double points) { using namespace std; bool pass = true; // Compare actual vs. expected sets if (actual.GetSize() != (int)expected.size()) { pass = false; } else { // Expected and actual have equal sizes, so compare contents for (const T& item : expected) { if (!actual.Contains(item)) { pass = false; } } } // Print the pass or fail message with expected and actual sets testFeedback << (pass ? "PASS: " : "FAIL: "); testFeedback << title << endl; StaticSetTestCase::Print<unordered_set<T>, T>(testFeedback, expected, ", ", " Expected: {", "}\n"); actual.Print(testFeedback, ", ", " Actual: {", "}\n"); return pass ? points : 0.0; } template <typename ContainerType, typename ItemType> static void Print(std::ostream& output, const ContainerType& iterable, std::string separator = ", ", std::string prefix = "", std::string suffix = "") { // Print the prefix first output << prefix; bool firstItem = true; for (const ItemType& item : iterable) { if (firstItem) { // Print first item without separator output << item; firstItem = false; } else { // Print item preceeded by separator output << separator << item; } } // Print the suffix last output << suffix; } virtual int Execute(std::ostream& testFeedback) = 0; }; // BinaryOpsTestCase represents an executable test case for the StaticSet's // Union(), Intersection(), and Difference() member functions template <typename T> class BinaryOpsTestCase : public StaticSetTestCase { private: std::unordered_set<T> setA; std::unordered_set<T> setB; std::unordered_set<T> expectedUnion; std::unordered_set<T> expectedIntersection; std::unordered_set<T> expectedAMinusB; std::unordered_set<T> expectedBMinusA; public: BinaryOpsTestCase(const std::unordered_set<T>& setA, const std::unordered_set<T>& setB, const std::unordered_set<T>& expectedUnion, const std::unordered_set<T>& expectedIntersection, const std::unordered_set<T>& expectedAMinusB, const std::unordered_set<T>& expectedBMinusA) { this->setA = setA; this->setB = setB; this->expectedUnion = expectedUnion; this->expectedIntersection = expectedIntersection; this->expectedAMinusB = expectedAMinusB; this->expectedBMinusA = expectedBMinusA; } virtual int Execute(std::ostream& testFeedback) override { using namespace std; // Print sets A and B first Print<unordered_set<T>, T>(testFeedback, setA, ", ", "A = {", "}\n"); Print<unordered_set<T>, T>(testFeedback, setB, ", ", "B = {", "}\n"); // Create the two StaticSet objects from the unordered_set objects StaticSet<T> staticSetA(setA); StaticSet<T> staticSetB(setB); // Create the union, intersection, and differences StaticSet<T> actualUnion = staticSetA.Union(staticSetB); StaticSet<T> actualIntersection = staticSetA.Intersection(staticSetB); StaticSet<T> actualAMinusB = staticSetA.Difference(staticSetB); StaticSet<T> actualBMinusA = staticSetB.Difference(staticSetA); // Verify that performing operations didn't change either StaticSet's size vector<tuple<StaticSet<T>*,unordered_set<T>*,string>> sizeChecks = { make_tuple(&staticSetA, &setA, "A"), make_tuple(&staticSetB, &setB, "B") }; for (auto& sizeCheckTuple : sizeChecks) { StaticSet<T>* staticSet = std::get<0>(sizeCheckTuple); unordered_set<T>* unorderedSet = std::get<1>(sizeCheckTuple); string staticSetName = std::get<2>(sizeCheckTuple); if (staticSet->GetSize() != (int)unorderedSet->size()) { testFeedback << "FAIL: Creating the union/intersection/difference "; testFeedback << " of StaticSets A and B changed set "; testFeedback << staticSetName << "'s size from "; testFeedback << unorderedSet->size(); testFeedback << " to " << staticSetA.GetSize() << std::endl; return 0.0; } } // Compare actual vs. expected sets double totalPoints = 0.0; totalPoints += StaticSetTestCase::CompareSets<T>(testFeedback, actualUnion, expectedUnion, "Union operation", 1.0); totalPoints += StaticSetTestCase::CompareSets<T>(testFeedback, actualIntersection, expectedIntersection, "Intersection operation", 1.0); totalPoints += StaticSetTestCase::CompareSets<T>(testFeedback, actualAMinusB, expectedAMinusB, "A \\ B operation", 0.5); totalPoints += StaticSetTestCase::CompareSets<T>(testFeedback, actualBMinusA, expectedBMinusA, "B \\ A operation", 0.5); return (int) totalPoints; } }; // UnaryOpsTestCase represents an executable test case for the StaticSet's // Filter() and Map() member functions template <typename T, typename MappedItemType> class UnaryOpsTestCase : public StaticSetTestCase { private: std::unordered_set<T> sourceSet; std::function<bool(const T&)> filterPredicate; std::function<MappedItemType(const T&)> mapFunction; std::unordered_set<T> expectedFiltered; std::unordered_set<MappedItemType> expectedMapped; public: UnaryOpsTestCase(const std::unordered_set<T>& sourceSet, std::function<bool(const T&)> filterPredicate, std::function<MappedItemType(const T&)> mapFunction, const std::unordered_set<T>& expectedFiltered, const std::unordered_set<MappedItemType>& expectedMapped) { this->sourceSet = sourceSet; this->filterPredicate = filterPredicate; this->mapFunction = mapFunction; this->expectedFiltered = expectedFiltered; this->expectedMapped = expectedMapped; } virtual int Execute(std::ostream& testFeedback) override { using namespace std; // Print the source set first Print<unordered_set<T>, T>( testFeedback, sourceSet, ", ", "Set = {", "}\n"); // Create the StaticSet object from the unordered_set source StaticSet<T> staticSet(sourceSet); // Create the filtered and mapped sets StaticSet<T> actualFiltered = staticSet.Filter(filterPredicate); StaticSet<MappedItemType> actualMapped = staticSet.Map(mapFunction); // Verify that performing operations didn't staticSetA's size if (staticSet.GetSize() != (int)sourceSet.size()) { testFeedback << "FAIL: Filtering and/or mapping StaticSet S changed "; testFeedback << "S's size from " << sourceSet.size(); testFeedback << " to " << staticSet.GetSize() << std::endl; return 0.0; } // Compare actual vs. expected sets double totalPoints = 0.0; totalPoints += StaticSetTestCase::CompareSets<T>(testFeedback, actualFiltered, expectedFiltered, "Filter operation", 1.0); totalPoints += StaticSetTestCase::CompareSets<MappedItemType>( testFeedback, actualMapped, expectedMapped, "Map operation", 1.0); return (int) totalPoints; } }; #endif

main.cpp

#include <iostream> #include <string> #include "StaticSetTestCases.h" using namespace std; int main(int argc, char *argv[]) { // First test case tests binary operations: union, intersection, and // difference BinaryOpsTestCase<int> binaryOpsTestCase( { 42, 63, 99, 32, 18, 77, 64, 50, 12 }, { 64, 16, 32, 8, 4, 1, 2 }, { 1, 2, 4, 8, 12, 16, 18, 32, 42, 50, 63, 64, 77, 99 }, // expected union { 32, 64 }, // expected intersection { 42, 63, 99, 18, 77, 50, 12 }, // expected A minus B { 1, 2, 4, 8, 16 } // expected B minus A ); int binaryTestCasePoints = binaryOpsTestCase.Execute(cout); cout << endl; // Next test case tests unary operations: filter and map UnaryOpsTestCase<string, int> unaryOpsTestCase( { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" }, [&](const string& str) { // Keep each string with a length <= 4 return (str.length() <= 4); }, [&](const string& str) { // Map from the string to the string's length return (int)str.length(); }, { "zero", "one", "two", "four", "five", "six", "nine", "ten" }, // expected filtered { 3, 4, 5 } // expected mapped ); int unaryTestCasePoints = unaryOpsTestCase.Execute(cout); cout << endl; cout << "Binary operations score: " << binaryTestCasePoints << " out of 3"; cout << endl; cout << "Unary operations score: " << unaryTestCasePoints << " out of 2"; cout << endl; return 0; }

 

Deliverable

Upload the following:

  1. Full source code (.cpp files or .txt files)

  2. Screenshot of the Console with the code executing

2024 - Programming 3 / Data Structures - Author: Dr. Kevin Roark