A preserved archive of the Logical Gamers community forums, 2009-2025. The original threads and posts, served read-only. Registration, posting and private messages are gone for good.

Think like a programmer: Simplifying redundacy in C (Beginner)

2.5k views · started by Riddle ·
#1
Think like a programmer: Simplifying redundacy in C (Beginner)
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.

  1. 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.


  2. 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):
#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;
}
[/20]


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.

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!

#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
}
[/32][/64]


Final Notes:
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:
#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;
}[/3][/32]
#2
Fucking ownage!

Keep it up!
#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.
#4
Riddle please keep this up. I've been grasping the basics a little more eaiser with these.
#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.
#6
Such a genius. -.-
This would be useful for me in the future, thanks :D
#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.
#8
Riddle your awesome!!! Keep up the good work ye?

Thanks,
Adam
#9
Riddle, your guides always seem to bring intelligence back into LG.
Thanks :)
#10
I had to modify the variables pretty heavily to get it to work in my old ass compiler from the 90s.

But, removed more redundancy by combing IF statements. Shortened by lessening the number of variables.

#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;
}[/3][/32]
#11
GAMEchief wrote:
I had to modify the variables pretty heavily to get it to work in my old ass compiler from the 90s.

But, removed more redundancy by combing IF statements. Shortened by lessening the number of variables.


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. :P

It could be simplified a little bit more utilizing one of your methods by simplifying another one of the If Statements:

#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;
}[/3][/32]


I didn't test this, but it should work. I just took out like 3 lines only though. >_<
#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"};[/3] 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.
#13
GAMEchief wrote:
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"};[/3] 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.


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.


char str1[] = "Some String";

Is the same as:
char* str1 = "Some String";


The modern version of getch(); is getchar();. It's supposed to be in stdio.h, I think. UNIX doesn't have conio.h so I can't compile your script with the clear screen command, and any other conio references.

I did several string tests in C, here's the 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();
}
[/x][/i][/i][/x][/i][/i][/5][/2][/3]


Here are the results:

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!
#14
Want me to send you conio.h?

Also, what compiler do you use? Does it have a Windows version? :D
#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.