Comparing what it looks like to build a body mass index calculator in Java and Python

Comparing what it looks like to build a body mass index calculator in Java and Python

This was an exercise for a computer science class I am taking in university, which examines object oriented programming but only in a Java context. Because I am very tired of Java programming, I convinced Leifur to help me write the exact same program in Python. (It took a few beers and some cajoling to get him to look at Java code, but it happened!)

Why a BMI (body mass index) Calculator!?

I would have rather designed a different project, because I find the BMI to be an unreliable and bothersome concept. However this was my first assignment for my Object Oriented programming class so I had no choice.

Here are the following specs for the program. I created a Java BMI calculator that accepts imperial or metric input within a certain range of values.

Metric ranges:

  • weight : 10kg to 300kg
  • height : 0.5m to 3m

Imperial ranges:

  • weight: 22lbs to 660lbs
  • 20inches to 120inches

Pseudocode

(super basic pseudo code)

  • ask the user to chose between metric and imperial systems
  • ask the user his first name
  • ask the user for height and weight
  • check to make sure the height and weight are in proper ranges
  • run calculations depending on the type of calculation required
  • output an answer based on the result and using information from Singapoream BMI Classification

Again, I did not chose any of this, these program specs were determined by my professor in the assignment.


How the two programs were created

I created the Java program over the course of a Sunday evening, taking breaks to watch bad reality tv and answer emails/phone calls. My first iteration in Java was buggy (giving the user a BMI's of "Infinity" by accident) and was about 250 lines of code long. The new and improved version is around 400 lines of code.

The code in Python was written alongside Leifur in an hour-long pair programming practice session. It was officially my first time pair programming, which was really exciting because I'd never done it before. My knowledge of Django/Python is fairly limited to templating and design, so it was interesting to hear about more rudimentary bare-bones Python 3 stuff. My friend Julia Evans has written on pair programming and how useful she finds it. I believe that in the right contexts, pair programming is really awesome.

1. Ask the user to chose between metric and imperial systems

(Note: in almost all my Java programs, I create a custom print class so that I don't have to copy-paste System.out.println(); a million times. I call these custom classes write() and writeln().)

In Java, I need something like this :

  • Run the program in the terminal, program welcomes the user
  • Terminal asks user to pick metric or imperial
  • Restart the question if the user picks something else
  • Move on to another question

In my public static void main(String[] args) {, I build a Scanner object called scan, save some Strings and doubles, create a boolean called isItImperial and ask the terminal to write the introduction. It's a lot of necessary boilerplate. In my copy of the .java file, the fun stuff doesn't happen until line 82:

	/* Let's set up a variable for metric or imperial
	 *   boolean isItImperial = false = metric 
	 */  boolean isItImperial = true = imperial

	boolean isItImperial; // sets the variable
	
    write(introduction); // writes the introduction
	
    isItImperial = pickNumerology(); // sets isItImperial to whatever the user choses within the method pickNumerology()

(I called the method pickNumerology because I thought it was humourous.)

So what is in pickNumerology? I know that it has to return a boolean that decides whether or not the program continues as a metric or an imperial calculator.

public static boolean pickNumerology() {
	String input1 = "Please choose whether you would like an Imperial or Metric calculator.";
	String truefalse = "Write \"i\" and press ENTER for an Imperial Calculator, write \"m\" and press ENTER for a Metric Calculator :";

	// Build a scanner object for this method to capture input
	Scanner scan = new Scanner(System.in);
	
	boolean isItImperial1 = false; 
	boolean sanityCheck = false;

	while (sanityCheck == false) {
		// ask the user to pick the system
		writeln(input1);
		writeln(truefalse);
		String yesorno = scan.nextLine();

		if (yesorno.equalsIgnoreCase("i")) {
			isItImperial1 = true;
			sanityCheck = true;
			writeln("Checking to see if you wrote \"i\" for imperial...");
		} else if (yesorno.equalsIgnoreCase("m")) {
			isItImperial1 = false;
			sanityCheck = true;
			writeln("Checking to see if you wrote \"m\" for metric...");
		} 
		else {
			writeln("I'm sorry, that does not make sense, please try again.");
			sanityCheck = false;
		}
	}
	return isItImperial1;
}

Note that I have writeln() statements everywhere. That's because debugging happened.

Now that you have seen the Java code for that, let's look at the equivalent Python code:

introduction = "\nHello, I am the Radical Body Mass Calculator. Let's begin.\n"
print(introduction)
firstname = input("Please enter your first name :")
numerology = pick_numerology()

So far so good. As expected, Python just has less boilerplate and setting up happens very quickly. Exactly as with the Java code, pick_numerology() is going to return a boolean either True or False.

def pick_numerology():
	choices = ('i', 'm')
	imperialormetric = "Please choose whether you would like Imperial or Metric units.\nWrite 'i' for imperial or 'm' for metric. "
	while True:
		is_it_imperial = input(imperialormetric)
		if is_it_imperial in choices:
			return is_it_imperial

Indeed, the Python pick_numerology() is 6 lines of code, whereas the Java pickNumerology() is 29. They perform exactly the same way. I haven't had much chance to use tuples in my coding so far, so it was fun to have a reason to use them. Thanks Leifur for thinking about them!

2. Ask the user his first name

This is the Java code:

    String inputname = "Please enter your first name :";
	writeln(inputname);
	String firstname = scan.nextLine();

This is the Python code:

	firstname = input("Please enter your first name :")

3. Ask the user for height and weight + 4. Check to make sure height and weight are in the correct range

My Java code here at first was really messy. I've fixed it up a bit. It's still probably real messy.

I'm very thankful so many amazing programmers follow me and are always willing to check for bugs!

From now on, I'm going to show the code assuming the user picked metric.

	if (isItImperial == false) {  // the user wants metric
			
            // ask the user for height
			write(inputheight + metric); // these are strings telling the user the units
			writeln();
			height = scan.nextDouble();
			
            // ask the user for weight
			write(inputweight + metric); // these are strings telling the user the units
			writeln();
			weight = scan.nextDouble();
			writeln();
			
            // make sure everything is in the right range
			if (checkMetricRange(height, weight)==true) {
				writeln(calculating);
				bmi = metricCalculations(height, weight);
				sanityCheckAgain = true;
			} else if (checkMetricRange(height, weight)==false) {
				writeln(error1);
				sanityCheckAgain = false;
			} 
		}
	}

I have two custom classes here - checkMetricRange() which takes as values the height and the weight, and metricCalculations() which also takes the height and the weight.

Checking the metric range looks as follows:

 /* checkMetricRange to make sure the range is within workable limits as 
 *  specified in the specs. 
 */
public static boolean checkMetricRange(double height, double weight) {
	/*
		Metric ranges: 
		* weight : 10kg to 300kg
		* height : 0.5m to 3m 
	*/
	boolean isThisPossible = false;
	boolean sanityCheck = false;

	double checkingHeight = height;
	double checkingWeight = weight;

	double minimumHeight = 0.5;
	double maximumHeight = 3;
	double minimumWeight = 10;
	double maximumWeight = 300;

	while (sanityCheck==false) {
		// If either the height or the weight is zero, terminate immediately
		if (checkingHeight==0 || checkingWeight==0) {
			sanityCheck = true;
		} 
		// It seems like height and weight are both nonzero, let's check out the ranges
		if (checkRangesHelperClass(minimumHeight, maximumHeight, checkingHeight)==true && checkRangesHelperClass(minimumWeight, maximumWeight, checkingWeight)==true) {
			isThisPossible = true;
			sanityCheck = true;
		} else {
			isThisPossible = false;
			sanityCheck = true;
		} 
	}

	return isThisPossible;
}

More custom classes : checkRangesHelperClass(min, max, thingy) which returns a boolean :

public static boolean checkRangesHelperClass(double min, double max, double checking) {
	boolean isThisPossible = false;
	if (checking>min && checking<max) {
		isThisPossible = true;
	} else {
		isThisPossible = false;
	}
	return isThisPossible;
}

This is a lot of code. The reason I use methods like this is because they are reusable no matter which calculator type is chosen, metric or imperial. I also get to use the checkRangesHelperClass  later in the program when I'm looking to classify the BMI.

Now let's compare with the Python code:

if numerology == 'm':
	weight = float(input("Enter your weight in kilograms : "))
	height = float(input("Enter your height in metres"))
	if check_metric_range(height, weight): 	
    	{{ I cut out a line of code here because it's going to be explored in part 5 }} 			 
    	sanityCheck = True
	else:
		print("That makes no sense. Please try again.")

Now that just seems absurdly small. That can't be it, can it?! At least, check_metric_range(height, weight) has to be reasonably long right?

Nope. It's 5 lines long:

def check_metric_range(height, weight):
	if 0.5 <= height <= 3:
		if 10 <= weight <= 300:
			return True
	return False

This is one of those moments that make me think hard about the value of teaching Java as the first object oriented language in university. At least with C (the Latin of programming languages) one learns about the fundamentals of object oriented languages the hard way. It's not like we are learning Java because it's more verbose and obnoxious... Or are we? Is there value in the additional boilerplate - does it actually teach programmers to be more thoughtful? I'm really not so sure.

Python has more abstraction yet it remains readable and clear. With Java, the program quickly becomes unreadable, and it's notJustBecauseCamelTypeIsHarderToRead.

5. Run calculations

Java metric calculations:

public static double metricCalculations(double height, double weight) {
	double bmi; 
	double heightsquared = height * height;
	bmi = (weight / heightsquared);
	return bmi;
}

Python metric calculations:

def metric_calculations(height, weight):
	heightsquared = height * height
	return weight / heightsquared

6. Return an output with the BMI and the Singaporean Classification

Java:

	write(firstname + ", your Body Mass Index is : ");
	write(finalbmi);
	writeln();
	displaySingaporeanClassificationMessage(bmi, firstname);		

This is the displaySingaporeanClassificationMessage()method:

/* display a Singaporean Classification Message based on the calculated BMI
 *  messages based on http://en.wikipedia.org/wiki/Body_mass_index
 */
public static void displaySingaporeanClassificationMessage(double bmi, String firstname) {
	String beginning = "According to the Singaporean BMI Classification, ";
	String low = " is at a serious risk of developing nutritional deficiencies and osteoporosis.";
	String okay = " is probably at a healthy BMI level.";
	String meh = " is at a moderate risk of developing heart disease, high blood pressure, stroke or diabetes.";
	String ooops = " is at a high risk of developing heart disease, high blood pressure, stroke or diabetes.";
	
	double lowlimit = 18.5;
	double mediumlimit = 23.0;
	double highlimit = 27.5;

	if (bmi < lowlimit) {
		writeln(beginning + firstname + low);
	} else if (checkRangesHelperClass(lowlimit, mediumlimit, bmi)) {
		writeln(beginning + firstname + okay);
	} else if (checkRangesHelperClass(mediumlimit, highlimit, bmi)) {
		writeln(beginning + firstname + meh);
	} else if (bmi >= highlimit) {
		writeln(beginning + firstname + ooops);
	}
}

In Python, the code is very simple, and in fact fits in the loop we made for checking the ranges:

if numerology == 'm':
	weight = float(input("Enter your weight in kilograms : "))
	height = float(input("Enter your height in metres"))
    if check_metric_range(height, weight): // I'd cut out the following from section 3+4
    	display_singaporean_classification_message(
			metric_calculations(height, weight),
			firstname
		)
		sanityCheck = True
	else:
		print("That makes no sense. Please try again.")

And the function which controls which BMI message is shown:

def display_singaporean_classification_message(bmi, firstname):
	beginning = "According to the Singaporean BMI classification, "
	if bmi <= 18.4:
		print(beginning + firstname + " will starve to death.")
	elif bmi <= 22.9:
		print(beginning + firstname + " is probably fine.")
	elif bmi <= 27.4:
		print(beginning + firstname + " is probably not fine.")
	else: 
		print(beginning + firstname + " is probably really not fine.")

As you can tell, I was losing patience with the strings after a while.


Conclusion?

I am a little annoyed in university's continued useage of Java in the classroom. Java is not a bad language, but it also just seems like an uninteresting way of learning programming. Python is powerful and abstract. C is C. Heck, even C++ is a serious language used all over the place. Java and the java virtual machine are headaches.

I'm interested in code, not boilerplate. In the end, my Java program was 400 lines, and the Python program Leifur helped me write is around 70.