Objective: Learn how to spot patterns and use them to reduce the length of your code.
Problem: Create a program that will find the Area, Radius, and Diameter of a circle when one of these values is given.
- Approach in reality.
First we need to know the equation we are working with. The area of the circle is described by the following equation: A = r^2 * pi
In real life, we just solve for any variable that's not given, i.e. if A = 5, then r = sqrt(A/pi) = 1.26 approx.
- Approach in a program.
Make an equation for each situation: when A is given, when D is given, and when R is given. Use if statements to move the information to the right equation.
Seems simple, right?
A functional C code for this would look like the following (I wont go much into details about the source code, because that's not the point of this tutorial):
Take a close look, can you spot redundancy in the code? It's pretty obvious that we repeat too many times similar sequences of commands. There are different ways in which we could shorten and organize our code. This depends a lot on the programmer's habits, so it varies from person to person on how they organize themselves and avoid redundancy.Code:#include <stdio.h> #include <math.h> int main() { //A = pi * r^2; A = pi * (d/2)^2 char buffer[20]; int selection = 0; float area = 0.0; float radius = 0.0; float diameter = 0.0; const float pi = 3.14159; printf("Calculate the Area, Radius, or Diameter of a circle\n"); printf("What is the information you have?\n"); printf("1. Area\n2. Radius\n3. Diameter\n"); printf("Enter a number 1-3: "); if(fgets(buffer, sizeof buffer, stdin) != NULL) { if(sscanf(buffer, "%d", &selection) == 1) { if(selection > 0 && selection < 4) { //The selection is a number between 1 and 3 if(selection == 1) { //We have the area, find the radius and diameter! printf("What is the value for the area? "); if(fgets(buffer, sizeof buffer, stdin) != NULL) { if(sscanf(buffer, "%f", &area) == 1) { //It's a valid value, lets compute! //r = a/pi radius = sqrt(area/pi); diameter = 2*radius; printf("Area = %.2f ; Radius = %.2f ; Diameter = %.2f\n", area, radius, diameter); //results up to 2 decimal digits } else { printf("That's not a valid number!\n"); } } } else if(selection == 2) { //We have the radius, find the area and diameter! printf("What is the value for the radius? "); if(fgets(buffer, sizeof buffer, stdin) != NULL) { if(sscanf(buffer, "%f", &radius) == 1) { //It's a valid value, lets compute! //A = r^2/pi area = radius*radius*pi; diameter = 2*(radius); printf("Area = %.2f ; Radius = %.2f ; Diameter = %.2f\n", area, radius, diameter); //results up to 2 decimal digits } else { printf("That's not a valid number!\n"); } } } else if(selection == 3) { //We have the diameter, find the area and radius! printf("What is the value for the diameter? "); if(fgets(buffer, sizeof buffer, stdin) != NULL) { if(sscanf(buffer, "%f", &diameter) == 1) { //It's a valid value, lets compute! //A = (d/2)^2/pi radius = diameter / 2; area = radius*radius* pi; printf("Area = %.2f ; Radius = %.2f ; Diameter = %.2f\n", area, radius, diameter); //results up to 2 decimal digits } else { printf("That's not a valid number!\n"); } } } } else { printf("No such option! BYE!\n"); } } } return 0; }
How Riddle would do it (this does not mean it's the best way to avoid the redundancy in this case!):
Take a look at this code and compare it with the previous one. You might notice that the new code is only a single line smaller than the previous code, but if you look at the size of each file, there's 0.7KB decrease in size the second code. In other words, the size of a code with barely a 100 lines decreased about 29% (without the comments, take them out if you want to see the size)! This is actually very good!
Final Notes:Code:#include <stdio.h> #include <math.h> #include <string.h> //We will use strcat from here #define PI 3.14159 //It's defined instead of declared as a const char typedef struct //Structure to store circle data in case we want to add more circles in the future { float area; float radius; float diameter; } Circle; int main() { int selection = 0; char a[] = "area"; char d[] = "diameter"; char r[] = "radius"; char str_f[64] = "What is the value for the "; //one of a, d, or r strings will be concatenated at the end of this string char buffer[32]; //buffer to get the inputs float tmp; //a temp var that will serve to hold the input for a while //declare our structure and initiate all the values of the vars Circle c; c.area = 0.0; c.radius = 0.0; c.diameter = 0.0; printf("Calculate the Area, Radius, or Diameter of a circle\n"); printf("What is the information you have?\n"); printf("1. Area\n2. Radius\n3. Diameter\n"); printf("Enter a number 1-3: "); if(fgets(buffer, sizeof buffer, stdin) != NULL) //User wrote something? { if(sscanf(buffer, "%d", &selection) == 1)//Did they input a int? { if(selection > 0 && selection < 4) //is the int between 1-3? { if(selection == 1)//if it's 1, output the right msg about area { strcat(str_f,a); printf("%s? ",str_f); } else if(selection == 2) { strcat(str_f,r); printf("%s? ",str_f); } else { strcat(str_f,d); printf("%s? ",str_f); } if(fgets(buffer, sizeof buffer, stdin) != NULL) { if(sscanf(buffer, "%f", &tmp)) { if(selection == 1) //if the selection was 1, calculate the radius with the given area { c.area = tmp; c.radius = sqrt(c.area/PI); c.diameter = 2*c.radius; } else if(selection == 2)//calculate the area { c.radius = tmp; c.area = pow(c.radius,2)*PI; c.diameter = 2*c.radius; } else //calculate the diameter { c.diameter = tmp; c.radius = c.diameter/2; c.area = pow(c.radius,2)*PI; } printf("Area = %.2f ; Radius = %.2f ; Diameter = %.2f\n",c.area,c.radius,c.diameter);//print values } else { printf("Invalid number!\n"); //if the input is not a number print this } } } else { printf("No such option! BYE!\n"); //if the selection is out of range or did not input number, print this } } } return 0;//end }
From here on you should avoid redundancy if possible. Sometimes it's not worth it, for example, for small programs such as this it won't make a difference at all. However, it's something that you should take into account before you start working on large programs, otherwise you will have too much wasted memory! You can also use functions and classes to simplify things. In this case, functions would actually make the program bigger (however, more organized).
Compiled under GCC and tested in Fedora.
All the work was done by me, Riddle, please don't copy, unless you link back.
GAMEchief lessen the redundancy further:
Code:#include <stdio.h> #include <conio.h> #include <math.h> #define PI 3.14159 int main() { char buffer[32]; char* info[3] = {"area", "diameter", "radus"}; float area = 0, diameter = 0, radius = 0, value = 0; int selection = 0, valid = 0; clrscr(); printf("Calculate the Area, Radius, or Diameter of a circle\nWhat is the information you have?\n1. Area\n2. Radius\n3. Diameter\nEnter a number 1-3: "); if (fgets(buffer, sizeof buffer, stdin) != NULL && sscanf(buffer, "%d", &selection) && selection > 0 && selection < 4) { printf("What is the value for the %s? ", info[selection - 1]); if (fgets(buffer, sizeof buffer, stdin) != NULL && sscanf(buffer, "%f", &value)) { switch (selection) { case 1 : area = value; radius = sqrt(area / PI); diameter = 2 * radius; break; case 2 : radius = value; area = PI * pow(radius, 2); diameter = 2 * radius; break; default : diameter = value; radius = diameter / 2; area = PI * pow(radius, 2); } printf("Area = %.2f\nRadius = %.2f\nDiameter = %.2f\n", area, radius, diameter); valid = 1; } else printf("Invalid number!\n"); } else printf("No such option!"); getch(); if (!valid) main(); return 0; }
Results 1 to 15 of 15
- 11 Nov. 2009 12:07am #1
Think like a programmer: Simplifying redundacy in C (Beginner)
Last edited by Riddle; 04 Jan. 2011 at 06:23am.
- 11 Nov. 2009 02:16am #2
Fucking ownage!
Keep it up!
- 11 Nov. 2009 02:27am #3
Thanks.
I just hope that people take advantage of these types of guides. I honestly haven't seen many guides working from the perspective I'm taking.
- 11 Nov. 2009 04:07am #4
Riddle please keep this up. I've been grasping the basics a little more eaiser with these.
- 11 Nov. 2009 04:21am #5
These topics aren't discussed much around the tutorials I've seen so far, so I guess LG members get an advantage over other people on this situation.
However, it is still necessary to understand and know how to apply basic commands in any language. Most languages follow the same logic as C, so there shouldn't be much problem moving what you learn from here to other languages.
I'll probably not write another tutorial like this in a while, but I'll post some sources of my practice programs.
- 11 Nov. 2009 02:15pm #6Such a genius. -.-
This would be useful for me in the future, thanks
- 11 Nov. 2009 04:57pm #7
By the way, if anyone takes the time to compile and test this, you'll find something very important about computers: floating point arithmetic sucks in binary.
- 11 Nov. 2009 05:24pm #8
- Join Date
- Nov. 2009
- Location
- In CL0V3R's Smoke Ball
- Posts
- 1,010
- Reputation
- 28
- LCash
- 200.00
Riddle your awesome!!! Keep up the good work ye?
Thanks,
Adam
- 11 Nov. 2009 10:15pm #9
Riddle, your guides always seem to bring intelligence back into LG.
Thanks
- 12 Nov. 2009 07:51am #10
I had to modify the variables pretty heavily to get it trk in my old ass compiler from the 90s.
But, removed more redundancy by combing IF statements. Shortened by lessening the number of variables.
Code:#include <stdio.h> #include <conio.h> #include <math.h> #define PI 3.14159 int main() { char buffer[32]; char* info[3] = {"area", "diameter", "radus"}; float area = 0, diameter = 0, radius = 0, value = 0; int selection = 0, valid = 0; clrscr(); printf("Calculate the Area, Radius, or Diameter of a circle\nWhat is the information you have?\n1. Area\n2. Radius\n3. Diameter\nEnter a number 1-3: "); if (fgets(buffer, sizeof buffer, stdin) != NULL && sscanf(buffer, "%d", &selection) && selection > 0 && selection < 4) { printf("What is the value for the %s? ", info[selection - 1]); if (fgets(buffer, sizeof buffer, stdin) != NULL) { if (sscanf(buffer, "%f", &value)) { switch (selection) { case 1 : area = value; radius = sqrt(area / PI); diameter = 2 * radius; break; case 2 : radius = value; area = PI * pow(radius, 2); diameter = 2 * radius; break; default : diameter = value; radius = diameter / 2; area = PI * pow(radius, 2); } printf("Area = %.2f\nRadius = %.2f\nDiameter = %.2f\n", area, radius, diameter); valid = 1; } else printf("Invalid number!\n"); } } else printf("No such option!"); getch(); if (!valid) main(); return 0; }
- 12 Nov. 2009 02:13pm #11
Very nice! Thanks for the contribution! I had completely forgotten about SWITCH Statements... but then again, I just started learning C and I haven't experimented with them.
It could be simplified a little bit more utilizing one of your methods by simplifying another one of the If Statements:
Code:#include <stdio.h> #include <conio.h> #include <math.h> #define PI 3.14159 int main() { char buffer[32]; char* info[3] = {"area", "diameter", "radus"}; float area = 0, diameter = 0, radius = 0, value = 0; int selection = 0, valid = 0; clrscr(); printf("Calculate the Area, Radius, or Diameter of a circle\nWhat is the information you have?\n1. Area\n2. Radius\n3. Diameter\nEnter a number 1-3: "); if (fgets(buffer, sizeof buffer, stdin) != NULL && sscanf(buffer, "%d", &selection) && selection > 0 && selection < 4) { printf("What is the value for the %s? ", info[selection - 1]); if (fgets(buffer, sizeof buffer, stdin) != NULL && sscanf(buffer, "%f", &value) == 1) { switch (selection) { case 1 : area = value; radius = sqrt(area / PI); diameter = 2 * radius; break; case 2 : radius = value; area = PI * pow(radius, 2); diameter = 2 * radius; break; default : diameter = value; radius = diameter / 2; area = PI * pow(radius, 2); } printf("Area = %.2f\nRadius = %.2f\nDiameter = %.2f\n", area, radius, diameter); valid = 1; } } else printf("Invalid number!\n"); } else printf("No such option!"); getch(); if (!valid) main(); return 0; }
- 12 Nov. 2009 08:21pm #12
Mmm, yes, that would work.
But the printf()/valid=1 statements need to be outside of the switch().
Is char* info[3] = {"area", "diameter", "radus"}; valid with your compiler? Mine is old as dust, and that was the only way I could get Arrays to set up. And for strings, I had to use char* instead of []. I like your modern version better, as that's what other programming languages use, but it wouldn't let me do so.
It'd be nice to know if modern compilers are also backwards compatible, so that my limited C skills aren't all a waste.
EDIT:
Also, I don't know the modern form of getch(). I had to include an entire conio.h to use it, so if stdio.h has something with a similar ability, you should prolly use it.
I was surprised fgets worked on my compiler, as I'd never heard of it before. I could have prolly made some better programs had I know of it.
- 12 Nov. 2009 11:11pm #13
Thanks for noticing, I missed a bracket when I edited it.
The array of strings does work on my compiler, perfectly. Thing is that in C you need to create an array of pointers to strings in order to create a array string.
Code:char str1[] = "Some String";
Code:char* str1 = "Some String";
I did several string tests in C, here's the code:
Code:#include <stdio.h> int main() { int i; int x; char* a[3] = {"Vanilla", "Nuttella", "Godzilla and Goku"}; char* b = "What's up?"; char c[2][5] = {"Hello", "Yo!"}; char d[] = "Character Array!"; printf("Test A:\n"); for(i=0; i<3;i++) { printf("%s\n", a[i]); } printf("\nTest B:\n"); printf("%s\n", b); printf("\nTest C:\n"); for(i=0; i<2; i++) { for(x=0; x<5; x++) { printf("%c", c[i][x]); } printf("\n"); } printf("\nTest C 2:\n"); for(i=0; i<2; i++) { printf("%s\n", c[i]); } /*printf("\nTest C 3:\n"); for(i=0;i<2;i++) { for(x=0;x<2;x++) { printf("%s\n", c[i][x]); //This will crash the program } }*/ printf("\nTest D:\n"); printf("%s\n",d); getchar(); }
Code:Test A: Vanilla Nuttella Godzilla and Goku Test B: What's up? Test C: Hello Yo! Test C 2: HelloYo! Yo! Test D: Character Array!
- 12 Nov. 2009 11:43pm #14
Want me to send you conio.h?
Also, what compiler do you use? Does it have a Windows version?
- 12 Nov. 2009 11:51pm #15
Oh, it's just that conio.h is designed for DOS, which is basically Windows only. UNIX shells work differently.
I'm using GCC, which is the standard C/C++ compiler. Most UNIX C/C++ programs are compatible with Windows, they only need a few tweaks.