ITEC120
lect01:
A First Program
Consider Krusteaze Pizza, where you ask a server:
-
Q: Yo, your 12″ pizza, what is its area?
In arithmetic, what is the answer? (What if we want an exact number?)
π6², or approx 3.14*6² ∼ 113.3ish
[punch in on calculator/Java:] 3.14 * (12/2) * (12/2) ∼ 113.04
-
Thanks bro. Hmm, what's the area of your 16″ pizza?
π*8^2, or approx 3.14*8^2 ∼ 210.5ish
[punch in on calculator/Java:] 3.14 * (16/2) * (16/2) =~ 200.96
-
What's the area of the 20″ ...(and so on)
So in a calculator, you can type in expressions — that's a simple program!
Well yes, exceedingly simple — it's a program which computes the
area of a 16" pizza. Programs don't become interesting until
we generalize them to be able to handle different inputs.
In this case, it would be convenient to just type in the diameter,
and have the computer calculate the rest.
In fact, in high school algebra, you did this:
A(d) = pi * (d/2)^2
This is a general case,
which abstracts d=12, d=14, d=18, and any other
diameter13.
How to translate this algebra into Java?
Attempt 1 (of quite a few):
pieArea(d) {
return 3.14 * (d/2) * (d/2);
}
|
To call this Java function, we'll write (in a moment) something like:
pieArea(12) (this evaluates to ...)
pieArea(14) (this evaluates to ...)
This definition is close, but not quite complete Java yet.
It does abstract the repeated calculation into a function,
which makes us happy. "d" is called the function's "formal parameter",
and when we call the function we pass the function a concrete "argument"
(e.g. 12 or 14 or 18).
This Java definitioon of pieArea is similar to
the way it's specified in
the arithmetic, but with some extra scaffolding:
curly-brackets around the body of the function,
the keyword return,
and a semicolon at the end of the return statement.
They are simply things Java requires you to add, to write the function.
While some programming languages might accept the above, Java requires
you to specify a bit more information:
it needs you do specify the type of the argument
given to the function,
as well as the type which gets returned by the function.
In this case, the type is "Number", or more specifically, double
(for `double-precision estimates'; we can tell it's an
estimate14
because clearly 3.14 is not the correct value of π).
Other types are integers (int),
true/false (boolean), or more
sophisticated types like Set or Dictionary or MouseEvent —
We'll get into all that over the course of the class!
Okay, back to our program; this is how we specify that the argument
d is a double,
and that the function returns a double:
Attempt 2:
double pieArea( double d ) {
return 3.14 * (d/2) * (d/2);
}
|
This is a plausible Java, and it actually runs.
Let's try it: From BlueJ, find the main window:
-
Click new Class...; name it PizzaServer.
This will create a box with that name,
which is a blueprint from which actual PizzaServers will be
created later.
-
Double-click on the box for the PizzaServer class;
this gives you an editor showing you the interior of the blueprint.
For now, Delete everything inbetween the first and last curly-braces.
-
Just before the very last closing-brace '}', enter a couple of
blank lines, and then paste in the function pieArea we wrote above.
-
Press the "compile" button. You should get a message at the bottom,
“Compilation successful: no syntax errors.”
-
Back in the main window, find the PizzaServer blueprint,
and right-click (mac: control-click), and from the popup menu
select the first item: new PizzaServer().
Give it a name (say, Jo).
You'll see a red box — Jo the PizzaServer — appear.
You have created an actual instance of the PizzaServer object (blueprint)!
-
Now we ask Jo questions about the area of different sized pizzas:
right-click Jo, and select pieArea from the pop-up menu.
You'll be asked for the input d, and then the answer presented.
-
Alternately , in the Codepad, you can type
jo.pieArea(12)
and see the answer.
Congratulations, you have written your program, gotten it running,
and even run a couple of cursory test cases!
Alas, just getting a function to run isn't enough.
Why not? Well,
consider coming across the following code (which does run):
double f( double r ) {
return 3.14 * r * r * r / 3;
}
|
Is this a good function?
Is the formula it computes correct?
We have no idea! If trying to compute the volume of a right-cone with
radius r, then it's correct; if trying to compute the surface area of the
cone then it's wrong, and if it's calculating something else entirely,
it may or may not be correct.
The Zeroth Law of Programming
Code is meant to be read by humans.
At the least, this law requires that each function must
have a clear description of what it computes.
Even if a computer might be able to do something with undocumented code,
it's useless to people
(including the person who wrote it, after a week,
when they don't remember exactly what each function does.)
(One very easy mistake to make with functions we've written,
is remembering that pieArea should be passed the pizza's diameter,
not the radius.
There is no way for the computer to help us remember this.)
We'll come back later to what comprises an adequate description.
Note that just the fact of choosing a good “self-documenting”
name goes a long way; avoid generic names like f or calculate or doIt.
But, just because a name seems self-documenting to you
doesn't necessarily mean that further comments aren't needed.
For example,
double rightConeSurfaceArea( double r )
|
This name is fairly descriptive about what the function returns.
But other programmers who use this function might still have questions:
Does this function include the cone's base, in the surface area?
Does it include the inside and outside of the cone
(which is important if painting an actual cone inside and out).
Okay, back to our pieArea example, Attempt 3, where we add a description.
Descriptions precede the function, and are enclosed by /* and */:
/** pieArea
* Given a nonnegative diameter d (in inches), return the surface
* area of the pizza top (in square inches).
* Accurate to within 1%.
*/
double pieArea( double d ) {
return 3.14 * (d/2) * (d/2);
}
|
This is the first acceptable version,
(though as we'll see later,
there are a couple of improvements yet in store).
A second function
Some customers are concerned with the surface area of their pizza.
Others prefer crusts, and want to ask the server how much blank crust
each pizza comes with.
The server happens to know that Krusteaz Pizzas have a 2″ border
of crust around them (what cheapskates!).
Let's write the function crustArea:
-
Step 1: What are some test cases?
We realize, we need to assume that a pizza-diameter is at least 4″,
before the 2″ border really makes sense.
The First Law of Programming
Work out test cases by hand before starting any code.
This helps makee sure you understand the task,
and by reflecting on your own thought processes to solve the problem
by hand, you'll be able to have an idea of how to proceed in the
general case.
Another benefit is that after writing the function,
you'll have test-cases (with solutioons) to check your function.
-
What formula will we use?
The Second Law of Programming:
Don't repeat code. Re-use existing code.
Why is re-using existing code good?
Consider the manager's great new marketing idea — square pizzas!
(or, triangular pizzas, or star-shaped, or rectangular in the golden ratio)
If the 2″ crust invariant is maintained, then when we change
pieArea, that's enough to keep the rest of our program correct.
15
Magic Numbers
What if the value or Pi changes?
Well, more particularly, we knnow that 3.14 is a poor approximation,
and we want to use the more accurate 3.14159.
What's wrong with doing a search-replace of '3.14' with '3.14159'?
-
What if 3.14 was used sometimes for pi, and sometimes it was
the price-per-pound of artichoke hearts?
-
What if you entered 31.4159, by mistake?
Later,
-
What if part of the program was already using 3.1416, for pi?
calculatioons wouldn't jibe — plus your search/replace might
leave you with '3.1415916'.
-
And after your were done with the search/replace, you still have
all these issues iff you suddently realize that pi is closer
to 3.141592653589793238462643383279
Solution:
Create a named constant, and attach it to the value 3.14;
then use that name instead of the literal digits "3.14", in the
rest of your program.
You might have
final double PI = 3.14159; /* The math constant. */
final double ARTICHOKE_COST_PER_POUND = 3.14; /* Cost in dollars. */
|
These lines tell Java "create the name ARTICHOKE_COST_PER_POUND,
and associate it with the value 3.14 forevermore (well,
until the program ends)".
Like functions, you need to attach the type (here, "double");
the keyword "final" assures this value won't change.
The Third Law of Programming
No magic numbers.
As a matter, of fact, π is such a common constant, that it already
comes pre-defined in Java: somewhere inside the Java libraries is a line
final double Math.PI = 3.14159265358979;
|
By convention, all-caps are used when naming constants.
(The prefix “Math.”
is actually a package name—we'll discuss packages
much later—so you don't think that a period
‘.’ is allowed
as part of ordinary Java names.)
Underscores are allowed in names, as in
ARTICHOKE_COST_PER_POUND.
(Spaces cannot be used—it would look like different words!
Hyphens, even though used in English for compound words,
cannot be used (at least in Java), since it would think you
are subtracting two different constants.)
(This guidelines are true for names of functions,
as well as names of constants.)
Let's revisit pieArea:
/** pieArea
* Given a nonnegative diameter d (in inches), return the surface
* area of the pizza top (in square inches).
* Accurate to within 1%.
*/
double pieArea( double d ) {
return 3.14 * (d/2) * (d/2);
}
|
-
replace the magic number 3.14 with its named version.
-
Is '2' a magic number?
Um, yes, but... gosh, it'd be kinda silly to have something like
final int RADII_PER_DIAMETER = 2; |
So, situations when magic numbers might be acceptable:
-
- The constants -1, 0, 1, 2 are often not to name.
The conversion from diameter to radius is one example.
But even then, sometimes these numbers are still worth naming:
final double STUDENT_DISCOUNT = 2.00; /* Discount in dollars. */
final int CARPOOL_THRESHOLD = 2; /* Min. #passengers to qualify. */
-
- Constants used in only one place, ever, in the entire program.
if (currentFontSize < 11) ...
The 11 here is a magic number; assigning it the name
TEXT_TOO_SMALL_THRESHOLD might arguably be overkill.
(Although, did you find this name makes that little code-snippet clearer?)
Use this exemption sparingly, though. Even when used just once,
it's more descriptive to name something:
Consider a video game
/* Gain a life for *each* multiple of this many points: */
final int POINTS_FOR_NEW_LIFE = 7500;
Even if 7500 were only used in one place in the program (the place
where points get incremented), this quantity is worth naming.
Not only is it a self-documenting name, but you might discover later
that it really does want to be used elsewhere—perhaps
in the game's help-pages!)
Note that 'magic number' can be other types of values than numbers;
notably, strings and characters can be magic constants as well.
Consider: perhaps you are playing a video game, and instead of
using 'j' to jump, the program lets you accomodate your left-handedness
and you set 'a' to be the jump command.
You read the help-documentation; certainly you want that documentation
to now read "...to jump, type 'a'".
Moreover, if you read "...to make a running jump, use 'control-j'".
If the documentation didn't know your preferences, it'd be unclear
whether you should use control-j, or control-a.
(And if you didn't remember/realize that you'd long ago remapped
the jump command, and internally the documentation used the magic
constant 'j' rather than an the named constant for the actual command,
you might be extremely frustrated at not being able to
finish the level!)
-
Do we have repeated code?
A little bit, yes!
“d/2” occurs twice,
and its purpose is the same: to convert
a pizza-diameter to a pizza-radius.
How to get rid of it?
We'll see later. It will be kinda like a named constant, except
that the we want the name to be local—recomputed every
time pieArea is called.
(A more extreme solution: write a function pizzaDiameterToRadius.
This is overkill, unless you find yourself doing this in multiple
places in your code.
Explain why it might be appropriate then.)
Readability tip: When writing comments,
start them with a capital letter and finish with a period.
(You don't need full sentences though; fragments are sometimes fine.)
See the above examples.
Why? We've been trained from age 5 to read with these conventions;
leverage this to make it that much easier on people reading your code
(which includes people grading your code!)
RE-CAP: Syntax.
-
Syntax for writing a function:
[return-type] [function-name]( [type] [formal-parameter-name] ) {
return [expression-involving-formal-parameter-name];
}
|
(Replace items in brackets, like [type],
with a particular type, like double or String.
Items which are not in brackets, like return and ;
must be typed literally.)
-
Syntax to declare a named constant (to avoid a magic number):
final [type] [name] = [expression];
|
Frequently, you'll find yourself defining a second constant who is
based on a first — e.g.
final int HEIGHT = 200; /* In pixels. */
final int WIDTH = HEIGHT * 2; /* In pixels. */
|
Use this if you want the width to vary proportional to the height.
If you want to fix the width as 400, regardless of somebody later tweaking
the height, you'd assign it to 400 directly
13well, non-negative diameter back
14Single-precision estimates are archaic
and we won't use them; they are called `float'. back
15
Note that sometimes,
code-reuse is easier said than done; if you'd named your function
'discArea' instead of 'pieArea', then the manager's shape-change would
require finding all places where 'discArea' was called,
and decide whether it was being used for pizza-area purposes, or
for other purposes.
back