The first thing that struck me when I looked at your code was that you had almost identical code to get user input for m
and n
.
When you think of what you are trying to in such blocks of code a bit carefully, you can easily transform them into functions and can be reused.
Instead of
bool valM, valN;
do{
std::cout << "m = ";
std::cin >> m;
valM = check(m, 'm');
} while(valM);
Use
int m = getInput("m = ", 'm');
However, you have some hard coded logic that assumes that the maximum value of m
is 3. You can remove the card coded magic number and let it be an input to getInput
.
int m = getInput("m = ", 'm', 3);
The signature of getInput
is:
int getInput(std::string const& prompt,
char name,
int max);
I have the following suggested implementation of getInput
. It follows your ideas but expresses the intent of what are trying to do more clearly, IMHO.
int getInput(std::string const& prompt,
char name,
int max)
{
int num = 0;
while ( true )
{
std::cout << prompt;
std::cin >> num;
if ( isValidValue(num, max) )
{
break;
}
printErrorMessage(name, num, max);
}
return num;
}
isValidValue
is simple to implement given the inputs.
bool isValidValue(int num, int max)
{
return ( num >= 0 && num <= max );
}
printErrorMessage
is not that difficult either.
void printErrorMessage(char name, int num, int max)
{
if (num < 0)
{
std::cout << "Negative error!" << std::endl;
}
if ( num > max )
{
std::cout << "Overflow error. (" << name << " > " << max << ")!" << std::endl;
}
}
I would suggest dividing getInput
further into two functions -- one function that gets the raw input and let getInput
perform the error checks before returning a valid input.
int getRawInput(std::string const& prompt)
{
int num = 0;
std::cout << prompt;
std::cin >> num;
return num;
}
int getInput(std::string const& prompt,
char name,
int max)
{
int num = 0;
while ( true )
{
num = getRawInput(prompt);
if ( isValidValue(num, max) )
{
break;
}
printErrorMessage(name, num, max);
}
return num;
}
In the event that you need to get multiple types of raw input, such as int
s and double
s, it can be easily converted to a function template.
template <typename T>
T getRawInput(std::string const& prompt)
{
T t = {};
std::cout << prompt;
std::cin >> t;
return t;
}
Here's the complete program that incorporates my suggestions. It works on my computer as you would expect with the limited amount of testing I subjected it to.
#include <iostream>
#include <limits>
// Ackermann function calculations
unsigned int ackermann(unsigned int m, unsigned int n){
if(m == 0)
return n+1;
if(n == 0)
return ackermann(m-1,1);
return ackermann(m-1,ackermann(m,n-1));
}
bool isValidValue(int num, int max)
{
return ( num >= 0 && num <= max );
}
void printErrorMessage(char name, int num, int max)
{
if (num < 0)
{
std::cout << "Negative error!" << std::endl;
}
if ( num > max )
{
std::cout << "Overflow error. (" << name << " > " << max << ")!" << std::endl;
}
}
template <typename T>
T getRawInput(std::string const& prompt)
{
T t = {};
std::cout << prompt;
std::cin >> t;
return t;
}
int getInput(std::string const& prompt,
char name,
int max)
{
int num = 0;
while ( true )
{
num = getRawInput<int>(prompt);
if ( isValidValue(num, max) )
{
break;
}
printErrorMessage(name, num, max);
}
return num;
}
int main(){
int m = getInput("m = ", 'm', 3);
int n = getInput("n = ", 'n', 12);
std::cout << "\nM = " << m << "\nN = " << n
<< "\n\nCALCULATING..." << std::endl;
std::cout << "A(" << m << ',' << n << ") = "
<< ackermann(m,n) << std::endl;
return 0;
}
My suggestions in a bulleted form:
Follow the DRY principle. If you find yourself repeating code, there is most likely a way to remove repetition by defining a function and using the function in multiple places.
Keep functions free of magic numbers as much as possible. In your case, the magic numbers 3 and 12 were moved to the calling function.
Give names to what you are doing (isValidInput
) and move them to their own functions.
Keep functions as atomic as possible (separation of checking whether the input is valid and printing an error message).
Make functions as generic as possible (converting getRawInput
to a function template).