|
| 1 | +# Assignment 6: Explore Courses |
| 2 | + |
| 3 | +Due Saturday, November 22nd at 11:59PM |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +In this assignment you will be exercising your understanding of `std::optional`. We'll be making use of the same `courses.csv` from assignment 1. You are tasked to write one function for this assignment, which attempts to find the a `Course` in the `CourseDatabase` object, and return it. |
| 8 | +You'll also explore the monadic operations that come with the `std::optional` class. Take a look at the code and review the `CourseDatabase` class to understand the interface. |
| 9 | + |
| 10 | +## Running your code |
| 11 | + |
| 12 | +To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit <kbd>Ctrl+\`</kbd> or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assignment6/` directory and run: |
| 13 | + |
| 14 | +```sh |
| 15 | +g++ -std=c++23 main.cpp -o main |
| 16 | +``` |
| 17 | + |
| 18 | +Assuming that your code compiles without any compiler errors, you can now do: |
| 19 | + |
| 20 | +```sh |
| 21 | +./main |
| 22 | +``` |
| 23 | + |
| 24 | +which will actually run the `main` function in `main.cpp`. |
| 25 | + |
| 26 | +As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track! |
| 27 | + |
| 28 | +> [!NOTE] |
| 29 | +> |
| 30 | +> ### Note for Windows |
| 31 | +> |
| 32 | +> On Windows, you may need to compile your code using |
| 33 | +> |
| 34 | +> ```sh |
| 35 | +> g++ -static-libstdc++ -std=c++23 main.cpp -o main |
| 36 | +> ``` |
| 37 | +> |
| 38 | +> in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with: |
| 39 | +> |
| 40 | +> ```sh |
| 41 | +> ./main.exe |
| 42 | +> ``` |
| 43 | +
|
| 44 | +## Part 0: Include `<optional>` |
| 45 | +
|
| 46 | +At the top of the `main.cpp` include `<optional>`, we're going to make use of `std::optional` in this assignment! |
| 47 | +
|
| 48 | +## Part 1: Write the `find_course` function |
| 49 | +
|
| 50 | +This function takes in a string `course_title`, and the function should try to find the `course` inside of the private `courses` member of the `CourseDatabase` object. What should the return type be? (hint: there may or may not be a `Course` for the `course_title` passed in) |
| 51 | +
|
| 52 | +> [!NOTE] |
| 53 | +> You need to change the type returned by `find_course` which is currenty `FillMeIn`. |
| 54 | +
|
| 55 | +## Part 2: Modifying the `main` function |
| 56 | +
|
| 57 | +Notice that we call the `find_course` here in the `main` function: |
| 58 | +
|
| 59 | +```cpp |
| 60 | +auto course = db.find_course(argv[1]); |
| 61 | +``` |
| 62 | +
|
| 63 | +Now, you need to make use of the [monadic operations](https://en.cppreference.com/w/cpp/utility/optional) to populate the `output` string properly. Let's walk through how to do this. |
| 64 | +
|
| 65 | +Here's the behavior that you want to recreate, **without using any conditionals** like `if` statements: |
| 66 | +```cpp |
| 67 | +if (course.has_value()) { |
| 68 | + std::cout << "Found course: " << course->title << "," |
| 69 | + << course->number_of_units << "," << course->quarter << "\n"; |
| 70 | +} else { |
| 71 | + std::cout << "Course not found.\n"; |
| 72 | +} |
| 73 | +``` |
| 74 | +
|
| 75 | +Very simply, if there is a course then the line at the bottom of `main` |
| 76 | +
|
| 77 | +```cpp |
| 78 | +std::cout << output << std::end; |
| 79 | +``` |
| 80 | +
|
| 81 | +Should produce: |
| 82 | +```bash |
| 83 | +Found course: <title>,<number_of_units>,<quarter> |
| 84 | +``` |
| 85 | +
|
| 86 | +if there is no course then |
| 87 | +
|
| 88 | +```cpp |
| 89 | +std::cout << output << std::end; |
| 90 | +``` |
| 91 | +
|
| 92 | +Should produce: |
| 93 | +```bash |
| 94 | +Course not found. |
| 95 | +``` |
| 96 | +
|
| 97 | +### Monadic Operations |
| 98 | +
|
| 99 | +There are three monadic operations: [`and_then`](https://en.cppreference.com/w/cpp/utility/optional/and_then), [`transform`](https://en.cppreference.com/w/cpp/utility/optional/transform), and [`or_else`](https://en.cppreference.com/w/cpp/utility/optional/or_else). Read the description of each of them in the lecture slides, and take a look at [the standard library documentation](https://en.cppreference.com/w/cpp/utility/optional). You will only need to use 2 of the mondadic operations. |
| 100 | +
|
| 101 | +Your code should end up looking something like this: |
| 102 | +
|
| 103 | +```cpp |
| 104 | +std::string output = course |
| 105 | + ./* monadic function one */ (/* ... */) |
| 106 | + ./* monadic function two */ (/* ... */) |
| 107 | + .value(); // OR `.value_or(...)`, see below |
| 108 | +``` |
| 109 | +
|
| 110 | +It can help to **think about what the type of `output` is and work backwards from there**. Pay attention to what each of the monadic functions does, as described in the hint below. |
| 111 | +
|
| 112 | +> [!NOTE] |
| 113 | +> Recall what the role is of each of the monadic functions. The official C++ library doesn't do a good job explaining this, so we have included a short reference here. Suppose `T` and `U` are arbitrary types. |
| 114 | +> |
| 115 | +> ```cpp |
| 116 | +> /** |
| 117 | +> * tl;dr; |
| 118 | +> * Calls a function to produce a new optional if there is a value; otherwise, returns nothing. |
| 119 | +> * |
| 120 | +> * The function passed to `and_then` takes a non-optional instance of type `T` and returns a `std::optional<U>`. |
| 121 | +> * If the optional has a value, `and_then` applies the function to its value and returns the result. |
| 122 | +> * If the optional doesn't have a value (i.e. it is `std::nullopt`), it returns `std::nullopt`. |
| 123 | +> */ |
| 124 | +> template <typename U> |
| 125 | +> std::optional<U> std::optional<T>::and_then(std::function<std::optional<U>(T)> func); |
| 126 | +> |
| 127 | +> /** |
| 128 | +> * tl;dr; |
| 129 | +> * Applies a function to the stored value if present, wrapping the result in an optional, or returns nothing otherwise. |
| 130 | +> * |
| 131 | +> * The function passed to `transform` takes a non-optional instance of type `T` and returns a non-optional instance of type `U`. |
| 132 | +> * If the optional has a value, `transform` applies the function to its value and returns the result wrapped in an `std::optional<U>`. |
| 133 | +> * If the optional doesn't have a value (i.e. it is `std::nullopt`), it returns `std::nullopt`. |
| 134 | +> */ |
| 135 | +> template <typename U> |
| 136 | +> std::optional<U> std::optional<T>::transform(std::function<U(T)> func); |
| 137 | +> |
| 138 | +> /** |
| 139 | +> * tl;dr; |
| 140 | +> * Returns the optional itself if it has a value; otherwise, it calls a function to produce a new optional. |
| 141 | +> * |
| 142 | +> * The opposite of `and_then`. |
| 143 | +> * The function passed to `or_else` takes in no arguments and returns a `std::optional<U>`. |
| 144 | +> * If the optional has a value, `or_else` returns it. |
| 145 | +> * If the optional doesn't have a value (i.e. it is `std::nullopt`), `or_else invokes the function and returns the result. |
| 146 | +> */ |
| 147 | +> template <typename U> |
| 148 | +> std::optional<U> std::optional<T>::or_else(std::function<std::optional<U>(T)> func); |
| 149 | +> ``` |
| 150 | +> |
| 151 | +> For example, given a `std::optional<T> opt` object, the monadic operations could be invoked as follows: |
| 152 | +> |
| 153 | +> ```cpp |
| 154 | +> opt |
| 155 | +> .and_then([](T value) -> std::optional<U> { return /* ... */; }) |
| 156 | +> .transform([](T value) -> U { return /* ... */; }); |
| 157 | +> .or_else([]() -> std::optional<U> { return /* ... */; }) |
| 158 | +> ``` |
| 159 | +> |
| 160 | +> <sup>Note that the `->` notation in the lambda function is a way of explicitly writing out the return type of the function!</sup> |
| 161 | +> |
| 162 | +> Notice that since each method returns an `std::optional`, you can chain them together. If you are certain that the optional will have a value at the end of the chain, you could call [`.value()`](https://en.cppreference.com/w/cpp/utility/optional/value) to get the value. Otherwise, you could call [`.value_or(fallback)`](https://en.cppreference.com/w/cpp/utility/optional/value_or) to get the result or some other `fallback` value if the optional doesn't have a value. |
| 163 | +
|
| 164 | +
|
| 165 | +
|
| 166 | +## 🚀 Submission Instructions |
| 167 | +
|
| 168 | +If you pass all tests, you are ready to submit! To submit the assignment: |
| 169 | +1. Please complete the feedback form [at this link](https://forms.gle/aGuFqLyhB18mNoPKA). |
| 170 | +2. Submit your assignment on [Paperless](https://paperless.stanford.edu)! |
| 171 | +
|
| 172 | +Your deliverable should be: |
| 173 | +
|
| 174 | +- `main.cpp` |
| 175 | +
|
| 176 | +You may resubmit as many times as you'd like before the deadline. |
0 commit comments