Lab Notes Nine: Procedures (Methods)
First, you can review lab eight. Next, we can discuss methods.

Methods are like recipes. They are generalized formulas.

Here's a recipe, that a human cook could use:

Mikey's Garlic Mashed Potatoes

Wash potatoes well, peel and quarter. Cook potatoes in boiling salted water until fork tender. Drain potatoes and return to the pot. Using a ricer or a masher, mash the potatoes slightly and set aside. In a saucepan, heat 1 tablespoon butter and half the cream and simmer and stir for 1 minute. Add contents of saucepan to the potatoes and stir well. On a low flame, stir in the rest of the cream and all of the roasted garlic. Finish with chives, salt and pepper. Serves four garlic lovers.

Next, we rewrite this recipe for a computer chef. Note the change of attitude.


Mikey's GMP

(In which the cook needs all the help that he can get!)

Note to the customer:

If you want GMP better bring your own ingredients!
Also, when you pass them to the cook pass them in the right order, and one by one.
Chef:
Check ingredients carefully, one by one.
If something doesn't match print error message and stop.

The necessary ingredients should be:

  1. one baking potato (call it p1)
  2. another baking potato (call it p2)
  3. some butter (call it b)
  4. some whipping cream (call it w)
  5. garlic (call it g)
  6. cream cheese (call it cc)
  7. chives (call it ch)
  8. salt (call it s)
  9. pepper (call it p)

Start cooking:

  1. wash p1.
  2. peel p1.
  3. quarter p1 and call qp1 the result.
  4. wash p2.
  5. peel p2.
  6. quarter p2 and call qp2 the result.
  7. get recipient (rec) and some water (wat).
  8. put wat, s, qp1 and qp2 in rec.
  9. identify free burner bu.
  10. put rec on bu and turn bu on.
  11. while (qp1 not fork tender and qp2 not fork tender) {
           wait 1 minute patiently
    }
  12. drain water out of rec and call bp what's left.
  13. identify masher m1 and take it.
  14. mash bp with m1, call bpm the result.
  15. take saucepan sp.
  16. divide cc into two halves: cc1 and cc2.
  17. add cc1 and b to sp.
  18. look at watch, record time now.
  19. compute then the time after one minute
  20. start simmering
  21. while ( current time less than then ) {
           stir
           wait 2 seconds
    }
  22. add bmp to saucepan sp
  23. locate spoon spoon and stir with it in sp
  24. add cc1 to saucepan sp
  25. stir with spoon in sp
  26. add g to sp and stir with spoon
  27. turn burner burner off
  28. place contents of sp into plate plate and call it gmp
  29. place ch on top of gmp and call it gmp:
    gmp += ch;
  30. gmp = gmp + s (add salt)
  31. return gmp
A few more considerations.

class Mikey {
  public static GarlicMashedPotatoes // this is the return type! 
                                     recipeOne( Potato p1, 
                                                Potato p2, 
                                                Butter b, 
                                                WhippingCream w, 
                                                Garlic g, 
                                                CreamCheese c, 
                                                Chives ch, 
                                                Salt s, 
                                                Pepper p) {

  // see the steps detailed above 
  ... 
} 
Testing the chef.

We set up the following situation:

class TestingTheChef {
  public static void main(String[] args) {
    // first get all the ingredients the chef needs 
    Potato myPotato1 = new Potato(); 
    Potato myPotato2 = new Potato(); 
    Butter myButter = new Butter(0.5); 
    ...
    Pepper myPepper = new Pepper();

    GarlicMashedPotatoes result; // get ready with a local plate 
    result = Mikey.recipeOne(myPotato1, myPotato2, myButter, ..., myPepper); 
  } 
} 
Now compile and run the classes. Bring your own plate to store the result.

Note:

  1. If you call the chef with an incorrect number of arguments (ingredients) then the chef won't be able to use this recipe and will report a compilation error and stop.

  2. If you call the chef with the expected number of ingredients but pass them to the chef in the wrong order the chef will also complain and stop.
This chef is following the instructions exactly as written.


What comes next is:

A201/A597 LAB ASSIGNMENT NINE

You are to build a small library of six methods. These methods will be all grouped in a class called LabNine. The six problems are listed below. For each problem there is a hint, or a bit of advice. For each problem you are encouraged to think of it as if it were Mikey's GMP recipe. Here now are the problems, followed by individual hints and pieces of advice.

Objective Develop a collection of commonly used methods.

Discussion In this lab you will be building up a small library of methods that we may find useful at some later time. A little time spent now, carefully considering how these methods ought to be written, can save considerable amounts of time later when code reuse can be employed.

While we will not always know the context in which a method will be used, we can generally make a reasonable guess. Whenever possible, we should try to make the implementation of a method as general as possible. This includes:

Task For each of the following functions and procedures (collectively known as methods in Java), write the method, and test it thoroughly before going on.

  1. A method that picks a random number between low and high, inclusive.

  2. A method that picks a random floating point number between low and high.

  3. A method to compute the distance between two Points.

  4. A method to compute the midpoint of two Points.

  5. A predicate to determine if two Circles overlap.

  6. A predicate to determine if two Rectangles overlap.

Assume the following types:
class Point {
  double x, y;
  Point(double x, double y) {
    this.x = x; 
    this.y = y;
  }
  public String toString() {
    return "I am a Point at (" + x + ", " + y + ")"; 
  }
} 

class Circle {
  private double radius, x, y; 
  Circle(double r, double x, double y) {
    radius = r; 
    this.x = x; 
    this.y = y; 
  }
  Point center() {
    return new Point(this.x, this.y); 
  }
  double radius() {
    return radius; 
  }
} 

class Rectangle {
  double x, y, width, height; 
  Rectangle(double x, double y, double width, double height) {
    this.x = x; 
    this.y = y;
    this.width = width;
    this.height = height; 
  }
} 
Here's a sample test of the methods, when you have them ready.

If you were to run the following tests

public static void main(String[] args) {
    for (int i = 0; i < 10; i++) 
        System.out.println(LabNine.random(-14, -8)); 
    for (int i = 0; i < 10; i++) 
        System.out.println(LabNine.random(3.2, 3.9));  
    Point p1 = new Point(1, 4); 
    Point p2 = new Point(5, 1); 
    System.out.println(LabNine.distance(p1, p2)); 
    Point p3 = LabNine.midpoint(p1, p2); 
    System.out.println(p3); 
    Circle c1 = new Circle(10, 3,  3); 
    Circle c2 = new Circle( 9, 3, 21); 
    System.out.println(
        "Circles overlap? The answer is: " + LabNine.overlaps(c1, c2));
    Rectangle r1 = new Rectangle(5, 5, 5, 5);  
    Rectangle r2 = new Rectangle(7, 1, 2, 2);
    System.out.println(
        "Rectangles overlap? The answer is: " + LabNine.overlaps(r1, r2)); 
}
You should obtain an output similar to this:
frilled.cs.indiana.edu%java LabNine
-13
-9
-10
-14
-8
-13
-12
-13
-8
-14
3.8893441296921716
3.8197796997135343
3.7198162871052696
3.5426342839050067
3.723866363497135
3.8542103463665556
3.762537621124611
3.862830975636286
3.397977654009052
3.2720480907723437
5.0
I am a Point at (3.0, 2.5)
Circles overlap? The answer is: true
Rectangles overlap? The answer is: false
frilled.cs.indiana.edu%

1. We go through three stages:

Inputs:
  1. an integer number low representing the lower endpoint of the interval
  2. an integer number high representing the higher endpoint of the interval
Outputs:
  1. a value that is random and in between low and high inclusive
Method:
First compute the number of possible outcomes in the interval:
int num = (high - low + 1); 
Then obtain a random number between 0 and 1.

double per = Math.random(); 
Scale this number to the interval we're working with.

int val = (int) (per * num); 
Translate the value into the desired range and return it.

return (val + low);
Code this in Java and place it in your class LabNine. Then in another class, which has the main method, invoke the method a few times (did you give it a name already?) to test it. You can, in fact, place the main in LabNine as well (which is what I did). Also, please note that you may have to adjust the size of the range if you want to include high as a possible outcome. Or maybe not.

2. This problem is very much related to the previous one.

So the description here will be a bit more general.

Inputs:

  1. an floating-point number low representing the lower endpoint of the interval
  2. an floating-point number high representing the higher endpoint of the interval
Outputs:
  1. a floating point value that is random and in between low and high
Note: by floating point we mean float or double, whichever suits you best.

Also, the problem doesn't say if the output includes high or not, so this is up to you.

Method:

It depends how you're generating the random numbers but most likely they will be random numbers between 0 and 1, let's call it val.

This value can be looked at as a percentage.

The length of the target interval is

(high - low)
so if we want to translate val into a location on the segment from low to high we will have to multiply the length of the interval by the percentage:
val * (high - low) 
This is a number that represents a fraction of the length of the target interval between low and high. If we add low to this we will obtain the desired random number that corresponds to val and falls in between low and high and we could return it:
return val * (high - low) + low; 
As we said it depends on the source you're using. The book says there are two such sources that you could use: Math.random or a Random object which could return nextFloat(). In both cases the method described here would work.
Now code this in Java, give it a name, place it in LabNine and call it a few times from the main of the other class (or LabNine's itself) to test it, just to make sure everything is OK.

3. Again, you are being asked to turn a solution in a procedure. How do you proceed. First solve the problem by itself, at least once. Then turn your solution into a procedure by looking at what's general in your solution. Here we need to compute the distance between two points.

How do you solve this problem? Here's a picture:

Here's an implementation:

public class Test {
    public static void main(String[] args) {
	Point p, q; 
	p = new Point(2, 4); 
        q = new Point(6, 1); 
        double distance; 
        int h, v; 
        h = Math.abs(p.x() - q.x()); 
	v = Math.abs(p.y() - q.y()); 
	distance = Math.sqrt(h * h + v * v); 
	System.out.println(distance); 
    } 
} 
Then we isolate the part that calculate the distance and place it in a method.

public class Test {
    public static void main(String[] args) {
	Point p, q; 
	p = new Point(2, 4); 
        q = new Point(6, 1); 
        double distance; 
        distance = LabNine.computeDistanceBetween(p, q); 
	System.out.println(distance); 
    } 
} 

class LabNine {
    public static double computeDistanceBetween(Point p, Point q) {  
        int h, v;
        h = Math.abs(p.x() - q.x()); 
	v = Math.abs(p.y() - q.y()); 
	return Math.sqrt(h * h + v * v); 
    }
}

Note that the code above assumes Point has two accessors, for the coordinates. If you don't implement them (and the class I provided does not have them, but they are easy to write so you can make the change yourself) then simply remove the parens, to work directly with the variables. It's up to you. Encapsulation is not the central point here, when we look at the distance method. But the preferred method would indeed be to have accessors with private instance variables (in general).

When we create a method we follow these steps:

  1. Write and test the code that implements the procedure.
    We've done this in Test.java
  2. Identify what the code needs (inputs) and what it produces (outputs).
    Our procedure needs the two points. It should have as the result the distance between the two points, most likely a number with decimals.
  3. The inputs are called parameters, and they have to be listed as formal parameters. They have types. Their number and order are important. They should be given names, used in the definition of the procedure.
    We call the parameters, the two points, p and q. Their type is Point.
  4. Come up with a descriptive name for the procedure (or method). The name of the procedure together with the number and types of the parameters (in the order in which they are listed) is called the signature of the method. There cannot be two methods with the same signature in the same class.
    We decided to call it computeDistanceBetween. Other names are possible.
  5. If the method returns a result, make sure you specify that in the definition of the method. Otherwise mark it as void (that is, as not returning a result).
    Our method returns a double.

The section above removes all mistery about it, but here's the overview anyway:

Inputs:

  1. Point p
  2. Point q
Outputs: Method:
Compute the distances between the points on the horizontal and on the vertical. Square each on of them, add them up and return the square root of the result. That is the distance between the two points by Pythagoras' theorem.

4. I will only provide the formula here and we can discuss this in class or during office hours.

Inputs:

  1. Point p
  2. Point q
Outputs: How similar this is with add defined as an instance method in Fraction!

Method:

Create a new Point whose coordinates are the averages of the coordinates of p and q and return it.One can achieve thist in just one line:
return new Point((p.x() + q.x())/2, (p.y() + q.y())/2); 

Same comment as above about using accessors vs. accessing the variables directly.

Again, we need to give it a name, code it in Java, place it in the LabNine class of methods and invoke it from main a few times for testing purposes.


5. Read Exercise R7.5 in the text to see what a predicate means.

Inputs:

  1. Circle c1
  2. Circle c2
Outputs: Method:
It should be clear that two circles overlap if and only if the distance between their centers is smaller than the sum of the two radii. The question is how do we get the center and the radius out of a Circle. You should be able to figure that out. So this method could also be described in one line (although a long one):
return ( LabNine.distance(c1.center(), c2.center()) 
                          <= 
                          c1.radius() + c2.radius()); 
Same comment as above about using accessors vs. accessing the variables directly.
So we use the method developed for problem 3.

Name it, code it, place it in LabNine use it a few times from main to test it.

6. Interesting problem with more than one solution.

Inputs:

  1. Rectangle r1
  2. Rectangle r2
Outputs: Method One:
As in the case of circles we need to work with the distance between the two possibly overlapping entities. Two rectangles overlap when the distance between their two centers on the horizontal is less than half of their combined widths and the distance between their two centers on the vertical is less than half of their combined heights.
Method Two:
The idea here is to realize that r1 does not overlap r2 if r1 is entirely to the north of r2 or entirely to the east of r2 or entirely to the south of r2 or entirely to the west of r2. (We have thus described the outside of r2). So now we need to return the negation of this, whatever truth value it may have.

Choose a method, code it in, place it in LabNine, test it. I used Method Two.


So these are the recipes.

Your main method should be using them to cook a banquet.


Last updated: Jul 15, 2001 by Adrian German for A201