CISC 3115
Modern Programming Techniques
Lecture 1
Prep
Overview
In this lecture we're going to present a series of applications that are somewhat different than the ones you've seen in 1115. We'll develop them
and discuss various issues as we go along.
Write an app named BankAccount that contains two methods named deposit and withdraw, both of which accept a balance and an amount
and returns the amount added-to/subtracted-from the balance respectively. The mainmethod should illustrate the methods
by declaring and initializing an integer variable named balance performing the following operations: first depositing $200,
then withdrawing $50, followed by a second withdrawal of $70.
We speak of modelling a bank account
We'll mention it again later, but in advance of the actual code, think about the difference between this and a corresponding
30-point question on the 1115 final.
class BankAccount {
public static void main(String [] args) {
int balance = 0;
System.out.println("Initial balance: " + balance);
balance = deposit(balance, 200);
System.out.println("Balance after deposit of $200: " + balance);
balance = withdraw(balance, 50);
System.out.println("Balance after withdrawal of $50: " + balance);
balance = withdraw(balance, 70);
System.out.println("Balance after withdrawal of $70: " + balance);
}
public static int deposit(int balance, int amount) {return balance + amount;}
public static int withdraw(int balance, int amount) {return balance - amount;}
}
Notes
This is a pretty lame app, even from the point of view of 1115.
The hardcoding of all the actions and operations makes the app useless for anything other than this simple sequence of
a deposit followed by two withdrawals.
The methods don't appear to do too much
OTOH, we're just getting warmed up
There ARE a couple of things we should remember:
We can't write the methods as:
public static void deposit(int balance, int amount) {balance += amount;}
Java passes primitives by-value which means that changes to parameters do not persist
upon returning to the calling method (i.e., the calling arguments are not changed by changes to the parameters).
The only way (for the moment at least) to have the change to a primitive parameter return to the calling method is to
return it explicitly in a return statement.
This also restricts us to returning only one such change (again for the moment).
To sort of 'add insult to injury', balance needs to be sent to the methods together with the amount of the transaction (and then assigned its new value from the return value of the method)
All methods must be declared static
Note the repetition of the transaction amounts
magic numbers
As an aside, a skill we need to develop is to start figuring out both the methods we need as well as their interfaces
(i.e., signatures)
Working with two or more bank accounts is possible (have more balance variables) but quickly proves unweildy, even introducing arrays
(we'll see that in a bit).
Do the methods buy us anything?
We have to send the balance and transaction amount to each; why not just do it in-place?
class BankAccount {
public static void main(String [] args) {
int balance = 0;
System.out.println("Initial balance: " + balance);
balance += 200;
System.out.println("Balance after deposit of $200: " + balance);
balance -= 50;
System.out.println("Balance after withdrawal of $50: " + balance);
balance -= 70;
System.out.println("Balance after withdrawal of $70: " + balance);
}
}
Notes
You can still discern the semantics of the app, but now it's only because of the names of the class, methods, and variables, and the string literals in the output
As an aside, there is no place for any logic that might be required by the deposit and/or withdrawal operations.
And here's what happens without meaningful names as well.
class C {
public static void main(String [] args) {
int i = 0;
System.out.println("Initial balance: " + i);
i += 200;
System.out.println("Balance after deposit of $200: " + i);
i -= 50;
System.out.println("Balance after withdrawal of $50: " + i);
i -= 70;
System.out.println("Balance after withdrawal of $70: " + i);
}
}
Notes
Now the only thing that reveals what the app is doing are the println's … remove those the you'd have absolutely
no idea what the purpose of the app was.
There's really no flavor of a bank account whatsoever anymore
Hard-coding the transactions is both boring and overly constraining; lets read them in from a file containing an action code followed by a transaction amount
import java.io.File;
import java.util.Scanner;
class BankAccount {
public static void main(String [] args) throws Exception {
Scanner scanner = new Scanner(new File("bank_actions.text"));
int balance = 0;
while (scanner.hasNext()) {
String action = scanner.next();int amount = scanner.nextInt();if (action.equals("D")) {
balance = deposit(balance, amount);
System.out.println("Balance after deposit of $" + amount + ": " + balance);
}
else if (action.equals("W")) {
balance = withdraw(balance, amount);
System.out.println("Balance after withdrawal of $" + amount + ": " + balance);
}
}
}
public static int deposit(int balance, int amount) {return balance + amount;}
public static int withdraw(int balance, int amount) {return balance - amount;}
}
Let's add some more semantics … something a bit more realistic … you can't withdraw from an account unless there's sufficient
money in the account.
import java.io.File;
import java.util.Scanner;
class BankAccount {
public static void main(String [] args) throws Exception {
Scanner scanner = new Scanner(new File("bank_actions.text"));
int balance = 0;
while (scanner.hasNext()) {
String action = scanner.next();
int amount = scanner.nextInt();
if (action.equals("D")) {
balance = deposit(balance, amount);
System.out.println("Balance after deposit of $" + amount + ": " + balance);
}
else if (action.equals("W")) {
balance = withdraw(balance, amount);
System.out.println("Balance after withdrawal of $" + amount + ": " + balance);
}
}
}
public static int deposit(int balance, int amount) {return balance + amount;}
public static int withdraw(int balance, int amount) {
if (balance >= amount) return balance - amount;elsereturn balance;
}
}
Notes
Notice how withdraw now has non-trivial logic along the lines of what we referred to
in the first implementation
The calling code does not change and is completely unaware of the semantics of this method
Is there a better alternative to returning the unchanged balance in the event of an 'invalid'
withdrawal?
On a related note, is a withdrawal larger than the current balance an error? … and
if so, what sort of error? … and what should be done about it?
Summary
Regardless of the variation, BankAccount was an implementation of something — a bank account.
There was a balance
There were methods that operated on that balance
In some sense, this app focues (or implements) a thing, i.e., a useful entity that one can manipulate (through the specified methods)
As opposed to the apps you coded in 1115, which focused on the processing of data, e.g., an app that reads in data, sorts it, finds the largest value and prints it
such an app is somewhat different (at least in perspective) from our BankAccount that maintains a balance and provides methods for manipulating that balance.
Some Things to Think About
What if you wanted two bank accounts?
What about twenty of them?
What about a savings account and a checking account?
For all of these, how would our implementation change?
Modelling Things with Several (Logically-Related) Elements: A Container (02-Container)
Write an app that reads values from a file and places them into a container, i.e., a data entity that contains other items of data. The
methods to be provided are:
add — adds a value to the container
find — searches the container for a value and returns the location at which the value was found and -1 otherwise
get — returns the value at a specified position in the container
size — returns the number of elements in the container
isEmpty — returns true if the container has no elements; false otherwise
print — prints out the container with surrounding {}'s and commas between the elements
import java.io.File;
import java.util.Scanner;
class Container {
public static void main(String [] args) throws Exception {
final int CAPACITY = 100;
int [] values = new int[CAPACITY];
int size = 0;
Scanner scanner = new Scanner(new File("container_actions.text"));
while (scanner.hasNext()) {
String action = scanner.next();
int value;
int index;
switch (action) {
case "A":
value = scanner.nextInt();
size = add(values, size, value);
System.out.print("After adding " + value + ": ");
print(values, size);
System.out.println();
break;
case "F":
value = scanner.nextInt();
int pos = find(values, size, value);
if (pos >= 0)
System.out.println(value + " is at position " + pos + " of the container");
else
System.out.println(value + " is not in the container");
break;
case "G":
index = scanner.nextInt();
value = get(values, size, index);
System.out.println("The value at location " + index + " is " + value);
break;
default:
System.out.println("*** Error *** unknown action: " + action);
}
}
}
public static int add(int [] values, int size, int value) {
values[size] = value;
size++;
return size;
}
public static int size(int [] values, int size) {return size;}
public static boolean isEmpty(int [] values, int size) {return size(values, size) == 0;}
public static int find(int [] values, int size, int value) {
for (int i = 0; i < size; i++)
if (values[i] == value) return i;
return -1;
}
public static int get(int [] values, int size, int index) {
return values[index];
}
public static void print(int [] values, int size) {
System.out.print("{");
for (int i = 0; i < size; i++)
System.out.print(values[i] + (i < size-1 ? ", " : ""));
System.out.print("}");
}
}
Notes
The container has a capacity (CAPACITY) as well as a size
(size)
The capacity is sometimes knows as the physical size and the size as the
logical size
Notice that in add we must return the new value of size (because of call-by-value), yet the contents of the values
array does indeed change (the new element is in the array upon returning from the method.
This is NOT an example of call-by-reference; we are still working with call-by-value. For an explanation, see
the discussions on References and Arrays and Methods in my 1115 lecture notes on Arrays
Notice the definition of isEmpty in terms of size.
This is an example of a delegation or wrapper
method
We did NOT handle the situation of an out-of-bound index sent to get
We also did NOT handle the situation of container overflow/
Note the comma-logic used in the print to properly place the commas
THis is an example of not-last-time logic
Summary
values and size work hand-in-hand to realize the container
They are both needed for the container to have any integrity.
They need to be passed in tandem to just about nay method dealing with the container
Again, multiple containers is cumbersome and you have to make sure that you never mix up the values array and
size variable of two different containers.
Model a point in 2-dimensional space (i.e., the 2D plane). A point is represented as a pair of values (we'll stick with integers … at least for now) representing
the x and y components of the point.
We will support the methods:
quadrant: returns a numeric value between 0 and 8 corresponding to the quadrant or
axis the point lies on
quadrantStr: returns a String value corresponding to the quadrant/axis the point lies on
import java.io.*;
import java.util.*;
public class Point {
public static void main(String [] args) throws Exception {
Scanner scanner = new Scanner(new File("points.data"));
System.out.println(" --- From the file");
System.out.println();
while (scanner.hasNextInt()) {
int
x = scanner.nextInt(),
y = scanner.nextInt();
doIt(x, y);
}
Random r = new Random(12345);
System.out.println();
System.out.println(" --- Some random points between 0 and 9");
System.out.println();
for (int i = 1; i <= 20; i++) {
int
x = r.nextInt(19)-9,
y = r.nextInt(19)-9;
doIt(x, y);
}
}
public static void doIt(int x, int y) throws Exception{
System.out.println(x + " " + y);
System.out.println("Quadrant: " + quadrantStr(x, y) + " (" + quadrant(x, y) + ")");
}
public static String quadrantStr(int x, int y) {
switch (quadrant(x, y)) {
case 0: return "Origin";
case 1: return "Quadrant 1";
case 2: return "Quadrant 2";
case 3: return "Quadrant 3";
case 4: return "Quadrant 4";
case 5: return "Positive x axis";
case 6: return "Positive y axis";
case 7: return "Negative x axis";
case 8: return "Negative y axis";
default: return "???";
}
}
public static int quadrant(int x, int y) {
if (x == 0 && y == 0) return 0;
if (x > 0 && y > 0) return 1;
if (x < 0 && y > 0) return 2;
if (x < 0 && y < 0) return 3;
if (x > 0 && y < 0) return 4;
if (x > 0 && y == 0) return 5;
if (x == 0 && y > 0) return 6;
if (x < 0 && y == 0) return 7;
if (x == 0 && y < 0) return 8;
return -1;
}
}
Notes
We illustrate this in some 'well-known values' (from the file):
We then follow up with some random points for 'completeness'
The argument to the Random objejct's nextInt
call is the range of the random number generated (0 &helliop; argument);
here, I chose a value that would (in conjunction with the -9 applied to the return value)
produce integers in the range -9 … 9
Model a line in 2-dimensional space (i.e., the 2D plane). A line is represented as two pair of values (i.e., points)
We will support the methods:
quadrant: returns a numeric value between 0 and 8 corresponding to the quadrant or
axis the point lies on
quadrantStr: returns a String value corresponding to the quadrant/axis the point lies on
length: returns the length of a line
isVertical: returns whether a line is vertical (infinite slope)
slope: returns the slope of a line
As usual, main reads in lines and demos the methods; the only difference here is the slope is only printed when the line has a slope; otherwise
Vertical line, has no slope is printed.
import java.io.*;
import java.util.*;
public class Line {
public static void main(String [] args) throws Exception {
Scanner scanner = new Scanner(new File("lines.data"));
while (scanner.hasNextInt()) {
int
x1 = scanner.nextInt(),
y1 = scanner.nextInt(),
x2 = scanner.nextInt(),
y2 = scanner.nextInt();
doIt(x1, y1, x2, y2);
}
Random r = new Random(12345);
System.out.println();
System.out.println(" --- Some random points between 0 and 9");
System.out.println();
for (int i = 1; i <= 20; i++) {
int
x1 = r.nextInt(19)-9,
y1 = r.nextInt(19)-9,
x2 = r.nextInt(19)-9,
y2 = r.nextInt(19)-9;
doIt(x1, y1, x2, y2);
}
}
public static void doIt(int x1, int y1, int x2, int y2) throws Exception {
System.out.println("(" + x1 + ", " + y1 + ") - (" + x2 + ", " + y2 + ")");
printQuadrantInfo(x1, y1);
printQuadrantInfo(x2, y2);
System.out.println("The length of the line is: " + length(x1, y1, x2, y2));
if (isVertical(x1, y1, x2, y2))
System.out.println("Vertical line, no slope");
else
System.out.println("The slope of the line is: " + slope(x1, y1, x2, y2));
System.out.println();
}
public static void printQuadrantInfo(int x, int y) {
System.out.println("(" + x + ", " + y + ") Quadrant: " + quadrantStr(x, y) + " (" + quadrant(x, y) + ")");
}
public static String quadrantStr(int x, int y) {
switch (quadrant(x, y)) {
case 0: return "Origin";
case 1: return "Quadrant 1";
case 2: return "Quadrant 2";
case 3: return "Quadrant 3";
case 4: return "Quadrant 4";
case 5: return "Positive x axis";
case 6: return "Positive y axis";
case 7: return "Negative x axis";
case 8: return "Negative y axis";
default: return "???";
}
}
public static int quadrant(int x, int y) {
if (x == 0 && y == 0) return 0;
if (x > 0 && y > 0) return 1;
if (x < 0 && y > 0) return 2;
if (x < 0 && y < 0) return 3;
if (x > 0 && y < 0) return 4;
if (x > 0 && y == 0) return 5;
if (x == 0 && y > 0) return 6;
if (x < 0 && y == 0) return 7;
if (x == 0 && y < 0) return 8;
return -1;
}
public static double length(int x1, int y1, int x2, int y2) {return Math.sqrt((Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)));}
public static boolean isVertical(int x1, int y1, int x2, int y2) {return x1 == x2;}
public static double slope(int x1, int y1, int x2, int y2) {return (y2 - y1) / (double)(x2 - x1);}
}
Model a pair of lines in 2-dimensional space (i.e., the 2D plane). A pair of lines line is represented as four pair of values (i.e., two lines of two endpoints)
We will support the methods:
quadrant: returns a numeric value between 0 and 8 corresponding to the quadrant or
axis the point lies on
quadrantStr: returns a String value corresponding to the quadrant/axis the point lies on
length: returns the length of a line
isVertical: returns whether a line is vertical (infinite slope)
slope: returns the slope of a line
isParallelogram: returns true of the quadrilateral is a parallelogram, i.e., opposite sides are equal
isRhombus: returns true if the quadrilateral is a rhombus, i.e., all four sides are equal
Again, main reads in the values, and illustrates the methods.
import java.io.*;
import java.util.*;
public class LinePair {
public static void main(String [] args) throws Exception {
Scanner scanner = new Scanner(new File("line_pairs.data"));
while (scanner.hasNextInt()) {
int
x11 = scanner.nextInt(),
y11 = scanner.nextInt(),
x12 = scanner.nextInt(),
y12 = scanner.nextInt(),
x21 = scanner.nextInt(),
y21 = scanner.nextInt(),
x22 = scanner.nextInt(),
y22 = scanner.nextInt();
doIt(x11, y11, x12, y12, x21, y21, x22, y22);
}
}
public static void doIt(int x11, int y11, int x12, int y12, int x21, int y21, int x22, int y22) throws Exception {
System.out.println("(" + x11 + ", " + y11 + ") - (" + x12 + ", " + y12 + ")" + " / " +
"(" + x21 + ", " + y21 + ") - (" + x22 + ", " + y22 + ")");
printQuadrantInfo(x11, y11);
printQuadrantInfo(x12, y12);
printQuadrantInfo(x21, y21);
printQuadrantInfo(x22, y22);
System.out.println("The length of the first line is: " + length(x11, y11, x12, y12));
System.out.println("The length of the second line is: " + length(x21, y21, x22, y22));
double m1 = slope(x11, y11, x12, y12);
double m2 = slope(x21, y21, x22, y22);
System.out.println("The slope of the first line is: " + m1);
System.out.println("The slope of the second line is: " + m2);
if (isParallel(x11, y11, x12, y12, x21, y21, x22, y22)) System.out.println("The two lines are parallel");
else if (isPerpendicular(x11, y11, x12, y12, x21, y21, x22, y22)) System.out.println("The two lines are perpendicular");
System.out.println();
}
public static void printQuadrantInfo(int x, int y) {
System.out.println("(" + x + ", " + y + ") Quadrant: " + quadrantStr(x, y) + " (" + quadrant(x, y) + ")");
}
public static String quadrantStr(int x, int y) {
switch (quadrant(x, y)) {
case 0: return "Origin";
case 1: return "Quadrant 1";
case 2: return "Quadrant 2";
case 3: return "Quadrant 3";
case 4: return "Quadrant 4";
case 5: return "Positive x axis";
case 6: return "Positive y axis";
case 7: return "Negative x axis";
case 8: return "Negative y axis";
default: return "???";
}
}
public static int quadrant(int x, int y) {
if (x == 0 && y == 0) return 0;
if (x > 0 && y > 0) return 1;
if (x < 0 && y > 0) return 2;
if (x < 0 && y < 0) return 3;
if (x > 0 && y < 0) return 4;
if (x > 0 && y == 0) return 5;
if (x == 0 && y > 0) return 6;
if (x < 0 && y == 0) return 7;
if (x == 0 && y < 0) return 8;
return -1;
}
public static double length(int x1, int y1, int x2, int y2) {return Math.sqrt((Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)));}
public static double slope(int x1, int y1, int x2, int y2) {return (y2 - y1) / (x2 - x1);}
public static boolean isParallel(int x11, int y11, int x12, int y12, int x21, int y21, int x22, int y22) {return slope(x11, y11, x12, y12) == slope(x21, y21, x22, y22);}
public static boolean isPerpendicular(int x11, int y11, int x12, int y12, int x21, int y21, int x22, int y22) {return slope(x11, y11, x12, y12) == -1/slope(x21, y21, x22, y22);}
}
Modelling Many Thing with Many Elements: Phonebook (Phonebook)
Given the file, "phonebook.text" containing phone entries in the format last-namephone-number, write an application that
allows the user to look up a phone number via the last name. You can assume no two entries have the same last name.
name? Weiss
789-012-3456
name? Arnow
123-456-7890
name? Washington
Not found
name? Jones
345-678-9012
name? Thrum
Not found
name? Thurm
012-345-6789
name?
import java.io.*;
import java.util.*;
public class Phonebook {
public static void main(String [] args) throws Exception {
final String FILENAME = "phonebook.text";
final int CAPACITY = 100;
String []
names = new String[CAPACITY],numbers = new String[CAPACITY];
int size = read(FILENAME, names, numbers, CAPACITY);
Scanner scanner = new Scanner(System.in);
System.out.print("name? ");
while (scanner.hasNext()) {
String name = scanner.next();
String number = lookup(names, numbers, size, name);
if (!number.equals(""))
System.out.println(number);
else
System.out.println("Not found");
System.out.print("name? ");
}
}
static int read(String filename, String [] names, String [] numbers, int capacity) throws IOException {
Scanner scanner = new Scanner(new File(filename));
int size = 0;
while (scanner.hasNext()) {
if (size == capacity) {
System.out.println("Phonebook capacity exceeded - increase size of underlying array");
System.exit(1);
}
names[size] = scanner.next();
numbers[size] = scanner.next();
size++;
}
return size;
}
static String lookup(String [] names, String [] numbers, int size, String name) {
for (int i = 0; i < size; i++)
if (names[i].equals(name)) return numbers[i];
return "";
}
}
Notes
Note the use of parallel arrays
Corresponding elements must be kept in synch
An Even More Extreme Case: Student Grading
Program 14.3
Write a program that reads in student data from a data file (whose name should be obtained from the user)
consisting of last name, first name, midterm, and final. The program calculates each student's average,
and grade, and prints out a roster consisting of all the information, together with whether the student performed above
or below the class average.
The program should define the following methods:
int read(String fname, String [] lastNames[], String [] firstNames, int [] midterms, int [] finals, int capacity);
void calcAverages(int [] midterms, int [] finals, double [] averages, int size)
double calcOneAverage(int midterm, int final)
double calcClassAverage(double [] averages, int size)
void calcGrades(double [] averages, char [] grades, int size)
char calcOneGrade(double average)
void printRoster(String [] lastNames, String [] firstNames, int [] midterms, int [] finals,
double [] averages, char [] grades, int size, double classAverage)
void printOneStudent(String lastName, String firstName, int midterm, int final,
double average, char grade, classAverage)
import java.io.*;
import java.util.*;
public class Program_14_3 {
public static void main(String [] args) throws Exception {
Scanner keyboard = new Scanner(System.in);
System.out.print("roster file? ");
String filename = keyboard.next();
final int CAPACITY = 100;
String []
lastNames = new String[CAPACITY],firstNames = new String[CAPACITY];
int []
midterms = new int[CAPACITY],finals = new int[CAPACITY];
double [] averages = new double[CAPACITY];
char [] grades = new char[CAPACITY];
int size = read(filename, lastNames, firstNames, midterms, finals, CAPACITY);
calculateAverages(midterms, finals, averages, size);
double classAverage = calculateClassAverage(averages, size);
calculateGrades(averages, grades, size);
printRoster(lastNames, firstNames, averages, grades, classAverage, size);
}
public static int read(String filename, String [] lastNames, String [] firstNames,
int [] midterms, int [] finals, int capacity) throws Exception {
Scanner scanner = new Scanner(new File(filename));
int size = 0;
while (scanner.hasNext()) {
if (size >= capacity) {
System.out.print("Reached array capacity and still more data. Make the array bigger");
System.exit(1);
}
lastNames[size] = scanner.next();firstNames[size] = scanner.next();midterms[size] = scanner.nextInt();finals[size] = scanner.nextInt();
size++;
}
return size;
}
public static void calculateAverages(int [] midterms, int [] finals, double [] averages, int size) {
for (int i = 0; i < size; i++)
averages[i] = calculateOneAverage(midterms[i], finals[i]);
}
public static double calculateOneAverage(int midterm, int finalExam) {return (midterm + finalExam)/2.0;}
public static double calculateClassAverage(double [] averages, int size) {
double total = 0;
for (int i = 0; i < size; i++)
total += averages[i];
return total / size;
}
public static void calculateGrades(double [] averages, char [] grades, int size) {
for (int i = 0; i < size; i++)
grades[i] = calculateOneGrade(averages[i]);
}
public static char calculateOneGrade(double average) {
return average >= 90 ? 'A' : average >= 80 ? 'B' : average >= 70 ? 'C' : average >= 60 ? 'D' : 'F';
}
public static void printRoster(String [] lastNames, String [] firstNames, double [] averages,
char [] grades, double classAverage, int size) {
for (int i = 0; i < size; i++)
printOneStudent(lastNames[i], firstNames[i], averages[i], grades[i], classAverage);
}
public static void printOneStudent(String lastName, String firstName, double average,
char grade, double classAverage) {
System.out.println(lastName + " " + firstName + " " + average + " " + grade + " " +
(average >= classAverage ? "Above" : "Below"));
}
}