CS 311 Fall 2024 > Assignment 3
CS 311 Fall 2024
Assignment 3
Assignment 3 is due at 5 pm Thursday, September 26. It is worth 65 points.
Procedures
This assignment is to be done individually.
Turn in answers to the exercises below on the UA Canvas site, under Assignment 3 for this class.
- Your answers must consist of two files:
da3.hpp
andda3.cpp
, from Exercises A, B, C, and D. Attach these files to your homework submission. - I may not look at your homework submission immediately. If you have questions, e-mail me.
Exercises (65 pts total)
General
This assignment is to be done individually.
In each of the following exercises,
you are to write a function or function template.
All functions & function templates are to be
in the files da3.hpp
and da3.cpp
.
The templates must be implemented entirely in the header file.
The non-templates must be prototyped in the header
and implemented in the source,
as usual.
Be sure to follow the coding standards. Standards 4A, 4B, and 4C now apply. You do not yet need to follow standards 4D or 4E.
In the files da3.hpp
and da3.cpp
,
you may include any other functions or classes that you wish.
These will not be tested;
however, they must follow the coding standards.
Also, use of the C++ Standard Library is legal in this assignment.
Skeleton Files
I have provided incomplete “skeleton” files
da3.hpp
and
da3.cpp
;
these are in the Git repository.
You may use these as the basis for your own work, if you wish.
Your project files will also include
llnode.hpp
,
which is in the Git repository.
File llnode.hpp
must not be modified!
Test Program
A single test program for all of the exercises
is available:
da3_test.cpp
.
If you compile and run the test program (unmodified!) with your code,
then it will test
whether your code works properly.
Note that your code will not compile with the test program unless all required functions exist. Therefore, you must write dummy versions of all functions—at least—or your work will not be graded. (The skeleton files make this easy; see above.)
The test program requires doctest.h
,
the header for the doctest unit-testing framework,
version 2.
Do not turn in the test program,
file llnode.hpp
,
or the doctest framework.
Exercise A — Linked List Lookup
Purpose
In this exercise you will write code to deal with a Singly Linked List. The code will signal an error condition—if one occurs—by throwing an exception.
Instructions
Write a function template lookup
,
prototyped as follows.
[C++]
template <typename ValueType> ValueType lookup(const LLNode<ValueType> * head, std::size_t index);
lookup
is given a pointer to a null-terminated Linked List (as discussed in class, and demonstrated in the fileuse_list.cpp
) and an integer index. It functions similarly to an array bracket operator, returning the item corresponding to the index, where the first item is numbered \(0\), the second \(1\), and so on, up to \(\mathit{size}-1\), where \(\mathit{size}\) is the number of items in the list. The data item is to be returned by value.- An empty (size zero) list will be given
to
lookup
by passing a null pointer as the parameterhead
. - If
index
is out of range—negative or at least size—thenlookup
must throw an exception of typestd::out_of_range
. lookup
must throw only ifindex
is out of range.- If an exception is thrown,
then the exception’s
what
member must return a string giving a brief description of the error. The message in the string must be a brief but informative one, aimed at a technical user with at least some knowledge of the source code.
The definition of LLNode
must be obtained by including file llnode.hpp
,
which is in the Git repository.
Exercise B — Did It Throw?
Purpose
This exercise is all about calling code that may throw an exception, and catching the exception appropriately.
Instructions
Write a function (not a template) didItThrow
,
prototyped as
void didItThrow(const std::function<void()> & ff, bool & threw);
std::function
is declared in the standard header
<functional>
.
Function didItThrow
is given either a function pointer,
or an object that acts like a function (that is, which has operator()
defined).
It then calls this function with no parameters.
If the function throws, then it sets parameter threw
to true
,
and passes the exception on to the caller.
If the function does not throw, then it sets paramete threw
to false
,
and returns normally.
Example usage:
void myFunc() { throw std::exception("Oh no!"); } bool result; try { didItThrow(myFunc, result); { catch (std::exception & e) { if (result) std::cout << "SUCCESS" << std::endl; else std::cout << "FAILURE" << std::endl; }
The above will print “SUCCESS
”
Other requirements:
ff
will take no parameters and return nothing (void
). But make no other assumptions about it.- In particular,
make no assumptions about the type of the exception that might be
thrown by
ff
.
Exercise C — Check Sorted
Purpose
In this exercise you will write a function that takes iterators as parameters and processes a range of data.
Instructions
Write a function template checkSorted
,
prototyped as
[C++]
template <typename FDIter> bool checkSorted(FDIter first, FDIter last);
Parameters first
and last
are two forward iterators
specifying a range in the standard manner.
Function checkSorted
returns
true
if the range is sorted in ascending order
by “<
”,
and false
otherwise.
Example usage:
[C++]
vector<string> v { "aardvark", "dog", "dog", "llama" }; bool result = checkSorted(v.begin(), v.end());
The above sets the variable result
to true
,
since the given vector
is in ascending order.
You may not modify the values in the range.
Exercise D — Recursive GCD
Purpose
In this exercise, you will implement a simple recursive algorithm.
Background
If \(a\) and \(b\) are two nonnegative integers, not both zero, then the greatest common divisor (GCD) of \(a\) and \(b\) is the greatest integer that evenly divides both \(a\) and \(b\).
For example, the GCD of \(910\) and \(42\) is \(14\), since both \(910\) and \(42\) are divisible by \(14\), and this is not true of any integer greater than \(14\). We write \(\gcd(910, 42) = 14\).
The GCD can be computed quickly based on the following rules.
- If \(a = 0\), then \(\gcd(a,b) = b\).
- Otherwise, if \(a > b\), then \(\gcd(a,b) = \gcd(b,a)\).
- Otherwise, \(\gcd(a,b) = \gcd(b \bmod a, a)\).
Above, \(b \bmod a\) is the remainder when \(b\) is divided by \(a\).
Here is how we would apply the above rules to compute \(\gcd(910, 42)\).
\[ \begin{align*} \gcd(910, 42) &= \gcd(42, 910) &&\text{by Rule 2}\\ &= \gcd(28, 42) &&\text{by Rule 3 [\(910 \bmod 42 = 28\)]}\\ &= \gcd(14, 28) &&\text{by Rule 3 [\(42 \bmod 28 = 14\)]}\\ &= \gcd(0, 14) &&\text{by Rule 3 [\(28 \bmod 14 = 0\)]}\\ &= 14 &&\text{by Rule 1} \end{align*} \]
The above method is a version of the Euclidean Algorithm, so called because it appeared in a text by the ancient Greek mathematician Euclid, written around 300 BC. It is thus among the oldest algorithms known.
Instructions
Write a function (NOT a template) gcd
,
prototyped as
int gcd(int a, int b);
- Function
gcd
is given two nonnegative integers, not both zero. It returns their GCD.So, for example
gcd(910, 42)
returns14
. - Function
gcd
must compute the GCD using the algorithm described above. - Function
gcd
must either be a recursive function, or it must do the bulk of its work by calling a recursive function. - Neither
gcd
nor any function it calls may contain a loop. - Whatever functions are called by
gcd
must not be available to the client code; that is, they must not be declared in the headerda3.hpp
.
Note that, in C++, \(b \bmod a\) is computed
using the %
operator:
b % a
.