C++ motivational thread

I’ll give that a go after work. Thanks.

And this morning, 3 hours later after picking up the trailer, which was still loaded (supposed to be empty) AND had a flat, I’m waiting for a guy to patch the tire. Funny how real world variables come into play.

The exercise that I last left off with here has turned into thinking about how multiple things might work. For example, all the statements of a for loop can be empty:

int i = 0;
for(;;){
	if(i < 10){
		cout << i << '\n';
		++i;
	}
	else if(i == 10)
		break;
}   

If that is ran without a break condition, it prints the numbers and just hangs out in the for loop afterward doing nothing, since it has no condition statement to be met. Seems to be, a dead end loop.

Similar while loop:

bool dead_end = true;
int i = 0;
while(dead_end == true){
	if(i < 10){
		cout << i << '\n';
		++i;
	}
}

Anyway, I’m building up something of a little reference of everything covered so far and best notions of how things might work, partly because I have a nagging feeling that I’m overlooking something covered that could be used to complete the exercise. Also, this chapter has been very vague on some things for my taste, which is prodding me to investigate things further outside of any covered details. If I have to camp out here for a bit to feel satisfied, I guess I’ll do that before moving on.

Yeah, while(true) is a standard infinite loop that’s used here and there, for(;;) does the same, but is less used because it doesn’t read as well.

I’m interested in the solution to this stage of the problem. It must be using techniques already taught. It must just be something like checking if the first character of the second string in an input pair is a number for it to be invalid.

I got scattered a bit on things today after work, so I’ll have to work on it tomorrow. And the woman will be at work this weekend, so I’ll probably spend most of the weekend with c++. Reading into a single variable as a string won’t really work, since whitespace is ignored. Reading into separate variables will have to be it. And yea, checking for numbers in the second seems to be the most straightforward way to check.

I was thinking of std::getline for the single string, but I do think the solution they are after are in the patterns you’ve been using.

Yea, that is what I’m thinking. I’m probably just overlooking it. And I took a peak tonight at the c++ reference link that you dropped here. No point in making my own reference with that available. It looks really good.

It’s good that it has examples for almost everything, some specifics here and there don’t have them yet, but somebody somewhere will contribute before long for the new stuff.

Back on it this morning. What I came up with for checking for missing unit:

#include "std_lib_facilities.h"
// Gets a length and unit and rejects number with missing unit

int main()
{
	double length;
	string unit;
	bool invalid_unit = false;

	cout << "\nEnter a length and a unit (in, ft, cm, m): ";
	while(cin >> length >> unit){
        //Checks each character of 'unit' to for number character (only need to check first really)
		for(int i = 0; i < unit.size(); ++i){
			int code = unit[i];
			if(code > 47 && code <  58)
				invalid_unit = true;
		}	
		if(invalid_unit == true)
			cout << "\nError! '" << unit << "' is not a valid unit\n";
		else
			cout	<< "\nLength == " << length
					<< "\nUnit == " << unit << '\n';
		invalid_unit = false;
		cout << "\nEnter a length and a unit (in, ft, cm, m): ";
	}
	
	return 0;	
}

The meat of it will be moved to a function. All needed patterns were in the book up to this point, but I wasn’t thinking of a string as being a sequence on which to use size(), or for getting a character of a string, i.e, unit[i]. The mention of string[x] likely prodded me think of it. Cheers Snookoda.

Update: The better way is to prompt for two separate inputs, since in the above code the user could enter a single number only and be confused by lack of a prompt for further input. Making use of an infinite loop here, I guess.

#include "std_lib_facilities.h"
// Gets a length and unit and rejects a number with a missing unit

int main()
{
	double length;
	string unit;
	bool missing_unit = false;

	
	while(missing_unit == false){
		cout << "\nEnter a length: ";
		cin >> length;
		cout << "\nEnter a unit: ";
		cin >> unit;
		for(int i = 0; i < unit.size(); ++i){
			int code = unit[i];
			if(code > 47 && code <  58)
				missing_unit = true;
		}	
		if(missing_unit == true)
			cout << "\nError! Missing unit.  '" << unit << "' is not a valid unit.\n";
		else
			cout	<< "\nLength == " << length
					<< "\nUnit == " << unit << '\n';
		missing_unit = false;
	}
	
	return 0;	
}

Good stuff. One thing would be that you are not breaking out of the for loop, which shouldn’t matter here but if you had a sneaky string that was e.g. 1,000,000 'A’s except for a 6 at the beginning then it would do 999,999 superfluous checks.

Yes, that’s an infinite loop. It would be clearer being while (true) since you never do break from the while in this code but the check implies that you will at some point.

I ditched from the while loop the ‘invalid_unit = false’ for a ‘true’. I wasn’t sure if that helped to make things more explicit, but I think it really doesn’t. A comment serves better there. When implementing the next component of the exercise (reject invalid units), it took care of the missing unit part. So I’m just commenting that part out in case I want to look at it later for other uses. Now I need to comb out this spaghetti a bit. I have variables haphazardly listed, and functions could likely help to give the main a good shave.

#include "../std_lib_facilities.h"
// Compares lengths of different units and converts to meters

int main()
{
	int char_code;
	double current_length;
	string current_unit;
	vector<string>valid_units = {"in", "ft", "cm", "m"};
	bool invalid_unit = false;
	double current_meters;
	double smallest_length;
	string smallest_unit;
	double largest_length;
	string largest_unit;
	double smallest_meters;
	double largest_meters;
	double previous_length;
	double previous_meters;
	constexpr double almost_equal = 0.01;
	string previous_unit;
	bool has_ran_once = false;

    // Keep it running forever, because everyone loves unit conversions
	while(true){
		// Get input
		cout << "\nEnter a length number: ";
		cin >> current_length;
		cout << "Enter a length unit (in, ft, cm, m): ";
		cin >> current_unit;
/*
		// Check for missing unit
		for(int i = 0; i < current_unit.size(); ++i){
			char_code = current_unit[i];
			if(char_code > 47 && char_code <  58)
				invalid_unit = true;
		}
*/
		// Check for unsupported unit type
		for(int i = 0; i < valid_units.size();++i){
			if(current_unit == valid_units[i]){
				invalid_unit = false;
				i = valid_units.size();
			}
			else
				invalid_unit = true;

		}
		if(invalid_unit == true)
			cout << "\nError! '" << current_unit << "' is not a valid unit.\n";
		else if(invalid_unit == false){
			// Convert current_length to meters
			if(current_unit == "cm")
				current_meters = current_length / 100;
			else if(current_unit == "m")
				current_meters = current_length;
			else if(current_unit == "in")
				current_meters = current_length * 2.54 / 100; // To cm, to meters
			else if(current_unit == "ft")
				current_meters = current_length * 12 * 2.54 / 100; // To in, to cm, to meters

			cout << "You entered: " << current_length << " " << current_unit << " (" << current_meters << " meters)\n";

			// First run, smallest_length and largest_length are the same
			if(has_ran_once == false){
				smallest_length = current_length;
				smallest_unit = current_unit;
				smallest_meters = current_meters;

				largest_length = current_length;
				largest_unit = current_unit;
				largest_meters = current_meters;

				previous_length = current_length;
				previous_unit = current_unit;
				previous_meters = current_meters;

				has_ran_once = true;
			}
			// After first run
			else if(has_ran_once == true){
				// Set smallest_length and largest_length so far
				if(current_meters < smallest_meters){
					smallest_length = current_length;
					smallest_unit = current_unit;
					smallest_meters = current_meters;
				}
				else if(current_meters > largest_meters){
					largest_length = current_length;
					largest_unit = current_unit;
					largest_meters = current_meters;
					}

				// Check equal
				if(current_meters == previous_meters)
					cout << "The current_length number is equal to the previous_length number.\n";
				// check if almost equal
				if(current_meters < previous_meters){
					if((previous_meters - current_meters) < almost_equal)
						cout << "The current_length number is almost equal to the previous_length number.\n";
				}
				else if(current_meters > previous_meters){
					if((current_meters - previous_meters) < almost_equal)
						cout << "The current_length number is almost equal to the previous_length number.\n";
				}

				previous_meters = current_meters;
				previous_unit = current_unit;
			}

			cout << "The largest value so far is: " << largest_length << " " << largest_unit << " (" << largest_meters << " meters)" << '\n';
			cout << "The smallest value so far is: " << smallest_length << " " << smallest_unit << " (" << smallest_meters << " meters)" << '\n';
		}
		invalid_unit = false;
	}

	return 0;
}

Re variables, you have the old way of declaring variables at the top of a function. Old C compilers needed that and it stuck, with humans making excuses for why they still did it afterwards (I think).

Best practice these days is declaring them as close to where they are used as is sensible. In your code, for example, all of the current_XXX variables can be declared inside the while loop. Same with valid_units, but there’s a reason for keeping that at the top of main or somewhere more visible since it’s important data that maybe should be visible in case you want to extend the list.

Declaring variables over and over in a loop also might seem wasteful, but optimizing compilers do a good job of identifying all sorts of things that can be… well… optimized so trusting the compiler is a good thing. Up to a point, as with everything.

Also you can if (invalid_unit) instead of adding == true, something which is true for any variable that can resolve to a boolean in some way, like a number where non-zero is true. In C++ you can also use not and and keywords in place of ! and && hieroglyphics. MSVC needs a compiler flag to enable standard C++ and to use them.

Also setting variables to the same value:

    // First run, smallest_length and largest_length are the same
	if (not has_ran_once) {
		smallest_length = largest_length = previous_length = current_length;
		smallest_unit = largest_unit = previous_unit = current_unit;
		smallest_meters = largest_meters = previous_meters = current_meters;

		has_ran_once = true;
	}

Watch out for the comments too:

// Check equal
if(current_meters == previous_meters)

This one is completely unnecessary because everybody knows what the very next line does and often naming variables and functions well means we don’t have to add comments at all.

Kind of the same with:

// After first run
else if(has_ran_once == true){

… although there and in the first else if you would be better off with just an else since there is no else or else ifs after the else if and it’s operating on a two state boolean anyhoo. In the case of using just an else that comment would be okay since there’s no expression describing what’s happening.

Of course none of that matters at the moment apart from having them in the back of your mind.

1 Like

Thanks for the tips. I’ll definitely try to keep that stuff in mind. Some of my comments are superfluous at this point, because I’ll use them to quickly layout how to do things and forget to yank them afterward. Comments also seem helpful for quickly spotting sections of code, just scanning over the text for lines starting with //.

While I’m thinking of it. One thing that has been helpful along the way when adding new functionality to code is getting the new functionality working in a new file that is very small with only minimal supporting code, then implementing that into the bigger file. I think I should probably be doing this in a more organized way. Things change a bit when implementing the new functionality, so it would be good to keep the smaller file updated to reflect those changes for later reference.

Working on another step of the exercise, adding keeping track of the total number of input lengths entered, the sum of those lengths, and moving the smallest and largest so far, at the end of the program. Seems simple enough. But it implies that the program should properly end, rather than ending when a double gets any char or string. I started on it last night and kept getting a range error, likely because I had abandoned working out new functionality in a new file first before implementing it.

Seemingly simple things makes the code grow like weeds. Ha ha. @Snookoda, you mentioned using ‘not’ for ‘!’. How do you do ‘!=’ that way? Edit: Nevermind. I guess that is only for the logical operators, not the comparison operators.

#include "std_lib_facilities.h"
// Loops getting numbers with options to continue or quit
// Sums all entered numbers and counts number of entries

double number_sum(double number, int count)
{
	static double total; // static not yet introduced in ch4
	if(count == 1)
		total = number;
	else if(count > 1)
		total += number;
	return total;
}

int main()
{
	bool run = true;
	double number = 0;
	int count = 0;
	char answer = '!';
	double sum;
	
	while(run == true){
		cout << "Enter a number: ";
		cin >> number;
		++count;
		cout << "The sum so far is: " << number_sum(number, count);
		cout << "\nThe number of entries is: " << count << '\n';
	
		cout << "\nEnter 'r' to run again or 'q' to quit: ";
		cin >> answer;
		if(answer == 'q'){
			run = false;
			cout << "Exiting...\n";
		}
		else if(answer != 'r'){
			while(answer != 'r' and answer != 'q'){
				cout << "\nEnter 'r' to run again or 'q' to quit: ";
				cin >> answer;
				if(answer == 'q'){
					run = false;
					cout << "Exiting...\n";
				}
			}
		}
	}
	return 0;	
}

Yes, having a sandpit file to play around in something I do as well. I usually call mine “sandpit”, not that this matters. :slight_smile:

I hope you don’t mind but I simplified that code, static variables like that are initialized once and are initialized to zero, but making it explicit is fine too.

// Loops getting numbers with options to continue or quit
// Sums all entered numbers and counts number of entries
#include <iostream>

using std::cin, std::cout;

double number_sum(double number)
{
	static double total = 0; // static not yet introduced in ch4
	return total += number;
}

int main()
{
	int count = 0;
	
	while (true) {
		cout << "Enter a number: ";
        double number;
		cin >> number;
		++count;
		cout << "The sum so far is: " << number_sum(number);
		cout << "\nThe number of entries is: " << count << '\n';
	    
        char command = '!'; // init to non-command character
        while (command != 'q' and command != 'r') {
		    cout << "\nEnter 'r' to run again or 'q' to quit: ";
		    cin >> command;
        }
		if (command == 'q') {
			cout << "Exiting...\n";
            break;
	    }
        // continue while loop because we have an 'r' command
	}
	return 0;	
}

… we can use break to break out of precisely one loop, if we’re in a nested loop we can only break to the next outer one so flags (or gotos (!)) are required there. Or refactoring.

See how the variables are declared right where we need them so are only visible in a smaller scope? Minimizing the scope variables are used in (with globals being the biggest sinners) is something to keep an eye on. Only count needs to exist in the outer scope because it needs to live longer than the while loop.

p.s. you’ll need to have -std=c++17 flag to compile that since it uses multiple using directives separated by commas, a C++17 feature.

p.p.s. It’s fashionable outside of tutorials to always use the namespace, e.g. std::cout, std::vector except sometimes where it’s a longer namespace nesting than that being omitted.

1 Like

I keep a ‘playground’ folder for working on the smaller bits that will get integrated with the whole. I need to figure out something for good organization though. Things are getting hairy. Even just naming things is going to take some experience to get good at.

And I don’t mind at all. Looking it over now. It’s a ton cleaner, as if it wasn’t written by a flailing n00b. Ha ha. Cheers.

I started out today (on another project) declaring variables close to where they would be used and ran into problems galore, not having a good eye yet for possible issues. So I ran back to listing them at the top for now.

I didn’t realize that on break. Definitely good to know.

Where we declare variables is all about thinking how long it needs to persist for and what else needs to be able to see it. Sticking stuff at the top is fine too, something working and not ideally arranged is better than something that’s broken!

I should add that I initialized the char command variable and not the double number because the latter is being used for input right away. The command variable is queried right away and can be initialized to any old garbage without being explicit. That garbage could be an ‘r’ or a ‘q’, so we make sure it isn’t.

Even though compilers will probably initialize it to zero, they don’t have to (psst, but the big ones do):

(you can change what compiler is used over in the right somewhere)

Doing some backtracking today to try to move things into functions. It isn’t going the smoothest. :smiley: If feels like using functions for the sake of using functions, not to improve the code.

Also, variable names in functions lean heavy to being the same name as the function, so I’m just using ‘result’ for now. For example, in ‘smaller()’, the variable ‘result’ really should be called ‘smaller’, but it seems ill to have the same name for that variable as the function name.

#include "std_lib_facilities.h"
// Numbers are steps of Ch4 drill in "Programming Principles and Practice Using C++" - second edition
// 1. Gets two ints and prints them
// 2. Print the smaller and larger values
// 3. Print, the numbers are equal, if equal
// 4. Input ints in program replaced with doubles
// 5. Prints the numbers are almost equal if they differ
//    by less than 1.0/100, after printing smaller and larger
// 5b. Moved comaprisons in program to functions

double smaller(double num1, double num2)
{
	double result;
	if(num1 < num2)
		result = num1;
	else if(num1 > num2)
		result = num2;
	
	return result;
}

double larger(double num1, double num2)
{
	double result;
	if(num1 > num2)
		result = num1;
	else if(num1 < num2)
		result = num2;
	
	return result;
}

bool equal(double num1, double num2)
{
	bool result = false;
	if(num1 == num2)
		result = true;

	return result;
}

bool almost_equal(double num1, double num2)
{
	constexpr double difference = 0.01;
	bool result = false;
	if(num1 > num2)
		if(num1 - num2 < difference)
			result = true;
	else if(num1 < num2)
		if(num2 - num1 < difference)
			result = true;
	
	return result;
}

int main()
{
	cout << "\nEnter two ints (enter '|' to exit): ";
	double num1, num2;
	while(cin >> num1 >> num2){
		cout << "You entered: " << num1 << " and " << num2;

		cout	<< "\nThe smaller value is: " << smaller(num1, num2)
				<< "\nThe larger value is: " << larger(num1, num2);

		if(almost_equal(num1, num2) == true)
			cout << '\n' << num1 << " and " << num2 << " are almost equal.";
		
		if(equal(num1, num2) == true)
			cout << '\n' << num1 << " and " << num2 << " are equal.";

		cout << "\n\nEnter two ints (enter '|' to exit): ";
	}

	return 0;	
}

Well, we kind of are just using functions mostly for the sake of it at this point! :slight_smile: But “everything” turns into calling functions/methods for this and that eventually and when the things we are putting into functions are non-obvious and/or reusable it makes much more sense.

Now that the code has been isolated in a function it’s cleaner though and easier to look at in isolation.

In smaller and larger you not handling equality and are not initializing double result, which can technically mean it’s init’d to garbage that exists in that memory position, but is probably zero. So you can pass 4.0 and 4.0 to them and get 0.0 (or garbage) back as the smallest/largest.

There are interesting solutions to this problem where a function might return an error or some other state when the inputs aren’t right, or that check input before the function is even run (last one not in C++ as part of the language, yet). But it might just be better to have an is_smaller/isSmaller/IsSmaller function to get a bool from to avoid this.

Watch out for “unreachable code” as well. If something is almost_equal it isn’t going to also be equal so that should be an else_if and if something is equal or almost_equal then we should not have output the smaller or larger value yet.

So putting all of that in reverse order equal/almost_equal/smallest|largest makes better sense.

There are other things that can tidy stuff up a bit, but they will be coming in future chapters (abs comes to mind, it returns a positive number if it gets a negative. Handy for the almost_equal function).

Thanks for the lookover and tips Snookoda. Much appreciated. I suppose it is just using functions for the sake of it, just to get used to organizing things that way. And yes, it actually is easier to read with things in functions, after having stepped away from it and came back for a glance over.

And I am missing an absolute value function for sure, but trying to stay within the limitations. There is no rule against knocking one up though, which I should be able to remember from working it out in Human Resources Machine. Trying to remember how that worked…