Writing a Good Program [The following guidelines use the word program but hold true just as well for code pieces, i.e., functions, classes, etc.] Programs should, optimally, be functional, elegant, efficient, user-friendly, and selfdocumenting. Why bother with programming style? Lots of reasons. ease in debugging to enable other programmers to follow your logic and the way it was carried through in the program. A program is usually used many times, by many different people. You may want to use your program again yourself in the future. A poorly written / documented program is hard to understand and does not lend itself to reuse. A well-written program is often more efficient and economical than a poorly written one. Modifications may be called for in the future. An unintelligible program is a monster to modify and debug. A. Functional - The program should do what it is called upon to do. It should be errorfree and produce output that is correct. B. Elegant - The program should do it in the best possible way. Avoid convoluted logic and awkward control mechanisms. break, continue, and goto statements are occasionally useful - employ them only when absolutely necessary. C. Efficient - The program should not be unnecessarily wasteful of computer resources, namely, time and space (memory). Avoid the use of extra variables and/or operations that are not needed for the functionality or readability of the program. D. User-friendly - The program should make as few assumptions as possible about the capabilities of the user. This means giving interactive user screens and printed reports a “natural” style and appearance. As always, we are trying to bring the machine up to the level of the human user (rather than the other way around). We use prompts to let the user know what kind of data is expected and in what format. Whenever possible we provide the user with a menu of options or possible data values to input. We validate the input data and provide helpful error messages (and a chance to enter the data again) if the input data is not in the correct range, format, etc. 106741325.doc 1 of 16 E. Self-documenting - The program should contain within the source code as much documentation information as possible. The program should be structured to highlight the major processes involved. Use descriptive names for variables, constants, functions, classes, objects, etc. Employ a recognized and consistent indenting scheme. Avoid unnecessary variables - too many variables not only waste memory but they also make programs harder to understand. Liberal use of comments: to explain what each variable name stands for; to clarify complicated calculations; to give information regarding the data needed by the program: data format, type, files used, etc. Comment block at the beginning of the program can contain: descriptive name for the program, author’s name, date written, date last modified, purpose of program. Assignment: Redo a programming assignment of your choice to make it as userfriendly and as self-documenting as possible. Submit together with your previous version of the assignment. 106741325.doc 2 of 16 Progressive Modification: Example Problem - Case #1 I have a sum of money to invest at a particular rate of interest for 10 years. How much money will I have at the end of 10 years, if compounded annually? What is my total interest earned? Analysis: a) output principal in 10 years P amount earned E b) input initial amount of money - P0 rate of interest R c) method - compound interest calculation - pseudocode or flowchart Pseudocode: (1) Start working on program. (2) Input amount, interest rate. (3) Calculate P1 (principal in year 1) as P0+ P0* R (4) Output principal for year 1 (5) Calculate P2 (principal in year 2) as P1+ P1* R (6) Output principal for year 2 (7) For each of the remaining years, N = 3 through 10, repeat steps (5) and (6), for principal in year N. (8) Calculate E: P10 - P0. (9) Output P10, E. (10) Stop working on program. START Flowchart: INPUT P0, R CALCULATE and PRINT P for each year CALCULATE P10 , E OUTPUT P10, E STOP 106741325.doc 3 of 16 //case1.cpp // Compound Interest Program - Case 1 // This program computes interest on some initial investment, // compounded annually for 10 years. #include <iostream> int main() { int year; float pzero, p, rate, earned; cout << "Starting principal? "; cin >> pzero; cout << "Interest rate? "; cin >> rate; cout << endl; p = pzero; for (year=1; year<=10; year++){ p = p + p * rate; cout << "After " << year << " years, you will have $" <<p <<endl; } earned = p - pzero; cout << "\nTotal interest earned: $" << earned <<endl; return 0; //successful termination } //end main Case 1 program output: Of course, we would really like the output to look like we are working with dollars and cents. We will use the stream manipulator functions of <iomanip>. 106741325.doc 4 of 16 cout << setiosflags (ios::fixed | ios::showpoint) << setw(20) << setprecision(2) << var << endl; //** Formatting for stream output** // fixed point | // always print decimal point // field width is 20 characters // 2 digits after decimal point // variable to be output // newline //case1a.cpp // Compound Interest Program - Case 1 - revised for nicer output // This program computes interest on some initial investment, // compounded annually for 10 years. #include <iostream> #include <iomanip> int main() { int year; float pzero, p, rate, earned; cout << "Starting principal? "; cin >> pzero; cout << "Interest rate? "; cin >> rate; cout << endl; cout << setiosflags(ios::fixed |ios::showpoint) << setprecision(2); p = pzero; for (year=1; year<=10; year++){ p = p + p * rate; cout << "After " << year << " years, you will have $" <<p <<endl; }//end for earned = p - pzero; cout << "\nTotal interest earned: $" << earned <<endl; return 0; //successful termination } //end main Much nicer! 106741325.doc 5 of 16 Case 2 -- Suppose I wish to perform these calculations for 5 different sets of input data: //case2.cpp // Compound Interest Program - Case 2 // This program computes interest on some initial investment, // compounded annually for 10 years. #include <iostream> #include <iomanip> int main() { int year; float pzero, p, rate, earned; for (int count = 1; count <=5; count++) { cout << "\n\n\tInvestment decision #" << count << endl; cout << "\nStarting principal? "; cin >> pzero; cout << "Interest rate? "; cin >> rate; cout << endl; cout << setiosflags(ios::fixed |ios::showpoint) << setprecision(2); p = pzero; for (year=1; year<=10; year++){ p = p + p * rate; cout << "After " << year << " years, you will have $" << p <<endl; } //end for earned = p - pzero; cout << "\nTotal interest earned: $" << earned <<endl; }//end for return 0; //successful termination of program } //end main 106741325.doc 6 of 16 Partial output: Partial output: 106741325.doc 7 of 16 Case 3 - Why limit the investment to 10 years only? //case3.cpp // Compound Interest Program - Case 3 // This program computes interest on some initial investment, // compounded annually for any number of years. #include <iostream> #include <iomanip> int main() { int n, year, years; float pzero, p, rate, earned; for (int count = 1; count<=5; count++) { cout << "\n\n\tInvestment decision #" << count << endl; cout << "\nStarting principal? "; cin >> pzero; cout << "Interest rate? "; cin >> rate; cout << "How many years? "; cin >> years; cout << endl; cout << setiosflags(ios::fixed |ios::showpoint) << setprecision(2); p = pzero; for (year=1; year<=years; year++){ p = p + p * rate; cout << "After " << year << " years, you will have $" << p <<endl; } //end for earned = p - pzero; cout << "\nTotal interest earned: $" << earned <<endl; }//end for return 0; //successful termination of program } //end main 106741325.doc 8 of 16 Partial Output: 106741325.doc 9 of 16 Case 4 - How about for N sets of data? //case4.cpp // Compound Interest Program - Case 4 // This program computes interest on some initial investment, // compounded annually for any number of years. #include <iostream> #include <iomanip> int main() { int n, year, years; float pzero, p, rate, earned; cout << "How many sets of data? "; cin >> n; for (int count = 1; count <=n; count++) { cout << "\n\n\tInvestment decision #" << count << endl; cout << "\nStarting principal? "; cin >> pzero; cout << "Interest rate? "; cin >> rate; cout << "How many years? "; cin >> years; cout << endl; cout << setiosflags(ios::fixed |ios::showpoint) << setprecision(2); p = pzero; for (year=1; year<=years; year++){ p = p + p * rate; cout << "After " << year << " years, you will have $" << p <<endl; } //end for earned = p - pzero; cout << "\nTotal interest earned: $" << earned <<endl; }//end for return 0; //successful termination of program } //end main 106741325.doc 10 of 16 Partial output: 106741325.doc 11 of 16 Case 5 - Producing a simple report //case5.cpp // Compound Interest Program - Case 5 // Using a report textfile // This program computes interest on some initial investment, // compounded annually for any number of years. #include <fstream> #include <iostream> #include <iomanip> int main() { int n, year, years; float pzero, p, rate, earned; ofstream cprn ("report.txt"); //File contains the report cout << "How many sets of data? "; cin >> n; for (int count = 1; count <=n; count++) { cout << "\n\n\tInvestment decision #" << count << endl; cout << "\nStarting principal? "; cin >> pzero; cout << "Interest rate? "; cin >> rate; cout << "How many years? "; cin >> years; cout << endl; cprn << setiosflags(ios::fixed |ios::showpoint) << setprecision(2); p = pzero; cprn << "\n\n\tInvestment decision #" << count << endl; cprn << "Starting Principal = $" << pzero <<endl << "Interest rate = " << rate <<endl<<endl; for (year=1; year<=years; year++){ p = p + p * rate; cprn << "After " << setw(3) << year << " years, you will have $" << p <<endl; } //end for earned = p - pzero; cprn << "\nTotal interest earned: $" << earned <<endl; }//end for cout << "See report in textfile \"report.txt\"\n\n"; return 0; //successful termination of program } //end main ofstream is a class defined in the fstream.h header file. We use it to define an object called cprn. When we use cprn in place of cout, the stream output is sent to the report.txt textfile rather than to the standard output (screen). This will become clearer when we write our own classes and objects. 106741325.doc 12 of 16 Interactive Output Window: 106741325.doc 13 of 16 Print the contents of report.txt: Investment decision #1 Starting Principal = $100.00 Interest rate = 0.05 After After After After After After After After After After 1 2 3 4 5 6 7 8 9 10 years, years, years, years, years, years, years, years, years, years, you you you you you you you you you you will will will will will will will will will will Total interest earned: have have have have have have have have have have $105.00 $110.25 $115.76 $121.55 $127.63 $134.01 $140.71 $147.75 $155.13 $162.89 $62.89 Investment decision #2 Starting Principal = $100.00 Interest rate = 0.15 After After After After After After After After After After After After After After After After After After After After 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 years, years, years, years, years, years, years, years, years, years, years, years, years, years, years, years, years, years, years, years, you you you you you you you you you you you you you you you you you you you you will will will will will will will will will will will will will will will will will will will will Total interest earned: 106741325.doc have have have have have have have have have have have have have have have have have have have have $115.00 $132.25 $152.09 $174.90 $201.14 $231.31 $266.00 $305.90 $351.79 $404.56 $465.24 $535.03 $615.28 $707.57 $813.71 $935.76 $1076.13 $1237.55 $1423.18 $1636.65 $1536.65 14 of 16 More on Formatting Program Ouptut Standard I/O - <iostream.h> cin cout I/O manipulators - use <iomanip.h> header file setfill(ch) set the fill character to ch setw(n) set the field width to n setprecision(n) set the floating-point precision to n places setiosflags(flags) set the format flags for the ios [input/output stream] Note: The setiosflags manipulator is also called a parametized manipulator, since it has parameters (arguments). format flags for setiosflags() [these flags may be combined by | operators] ios::showpoint always show the decimal point (default is 6 decimal digits) ios::showpos display a leading + sign when the number is positive ios::fixed display up to 3 integer digits and 2 digits after the decimal point. (For larger values, use exponential notation.) ios::scientific use exponential notation to display output ios::showbase show base indicator on output ios::left left justify output ios::right right justify output ios::skipws skip whitespace on input ios::fixed forces all subsequent numbers placed on the cout stream to be placed as if they were floating point values. ios::showpoint is then used to always display a decimal point. Since we often want both of these flags set, we will frequently joint them with the OR operator: setiosflags(ios::fixed || ios::showpoint). Then, setprecision(2) is used to ensure that (say) 2 decimal digits will always print even for numbers like 4 (4.00) or 4.1 (4.10). Flag in the current usage, a flag sets a certain condition as active. Activating one flag (e.g. right) may automatically deactivate other flag(s) (e.g. left). (Bronson p.132) Examples: [with ios::fixed flag set] manipulator(s) number display comments setw(2) setw(2) setw(2) setw(2) | 3| |43| |143| |2.3| number fits number fits field width field width setw(5) 106741325.doc 3 43 143 2.3 in field in field ignored ignored field width 5 15 of 16 setprecision(2) 2.366 | 2.37| setw(5) setprecision(2) 42.3 | 42.3| setw(5) setprecision(2) 142.364 |142.36| 106741325.doc w/ 2 decimal digits number fits in field field width ignored but precision spec used 16 of 16