CMPSCI 187: Programming With Data Structures
============================================

Today's topics
--------------

-   administrivia
-   introduction to stacks, again
-   generics
-   exceptions
-   programming by contract
-   Stack v. Stack

Administrivia
=============

Exam
----

Next week (Thursday 19 Feb) at 7pm in Marcus 131.

TELL US ABOUT CONFLICTS NOW!

Introduction to stacks, again
=============================

About stacks
------------

A *stack* is a data structure that holds a collection of objects.

Objects can be added using a *push* operation, and removed (and
retrieved) using a *pop* operation, which returns the objects in the
reverse order that they were pushed, removing them from the stack.

We can also *peek* at the top item, returning it without removing it.

Under- and overflow
-------------------

Popping or peeking an empty stack causes *stack underflow*, an error
usually signaled by an exception in Java. We can avoid this by checking
if the stack *isEmpty* before a peek or pop.

Some stacks are *bounded*, which means they have a fixed maximum
capacity. Pushing onto a full stack causes *overflow*. Like underflow,
we can check for this in advance.

Clicker question: Stacks 1
--------------------------

Clicker question: Stacks 2
--------------------------

More on stacks
--------------

Stacks are named after the real-world equivalent where you stack things
one atop the other -- blocks in a tower, plates in a ... stack. Only the
top thing is accessible.

You use them all the time, as they maintain the state of your program
when you call method after method in something called the *call stack*
(sometimes the *method stack* in Java).

When you call a method, the Java runtime must remember the context of
the current method to be able to restore context. When the second method
calls the third, the context of the second must be saved (and restored
when the third returns) and so on.

``` {.java}
int double (int i) {
  result = i;
  result = result * 2;
  return result;
}

int doubleSum(int[] a) {
  int sum = 0;
  for (int i=0; i<a.length; i++) {
    sum += double(a[i]);
  }
  return sum;
}
```

(on board)

The call stack grows each time a method is called, and shrinks each time
one is returned. Important: the call stack is bounded: if one method
calls another call another etc., without returning, eventually Java will
raise an exception when the call stack overflows.

Knowing about how this works will help you when we learn about
*recursion*, where a method calls itself repeatedly to solve a problem.

Clicker question: recursion 1
-----------------------------

Clicker question: recursion 2
-----------------------------

More uses of stacks
-------------------

Stacks provide a simple way to reverse the order of a collection. Push
everything on, then pop everything off.

Stacks can help us sort. We'll see this in the next discussion.

In a mathematical expression, each parenthesis must be balanced by
another. More generally, each open parenthesis of a certain
type(regular, square, angle, curly, etc.) must be balanced by another
close parenthesis of the same type, without an intermediate close of
another type.

So: [ (x + 2) - (x + {1 / 3})] is properly balanced, but [ (y - 4} /
[1 + 3) ] is not, nor is (a + [b - c) ]. Just counting doesn't work!

Verifying this is a *parsing problem* that is trivial with stacks. We'll
also examine harder parsing problems later.

Clicker question: parentheses
-----------------------------

Generics
========

Collections and types
---------------------

Remember StringLog? It holds a collection of Strings:

``` {.java}
class ArrayStringLog implements StringLog{
  String name;
  String[] log;
  int lastIndex = -1;
  ...
```

We could also write an IntegerLog, a DogLog, and so on, each time we
wanted to store a different kind of object. But that's absurd -- the
code is identical except for the type of the object being stored.

An extraordinarily dumb idea
----------------------------

(This is a straw man. DO NOT DO THIS. In fact, maybe don't even write it
down.)

We could build an ObjectLog that holds any kind of object.

Good news: Users of this class can put anything into it, since all
objects have Object as a superclass.

Bad news: Users of this class can put anything into it, since all
objects have Object as a superclass. The compiler essentially knows
nothing about the class of the objects stored in an ObjectLog.

Suppose our `ObjectLog` had a method to return the *i*th object from the
log, `Object get(int i)`. And let's say we're storing Strings in our
ObjectLog. How do we use the String we return? We have to *cast* it so
the compiler knows it's a String:

``` {.java}
String w = (String)log.get(0);
```

But if we make a mistake here, the runtime will throw a
`ClassCastException`!

The compiler should be able to check this for us -- it's a
straightforward property.

Generics
--------

You can parameterize methods -- where you can pass in one of many
*values* to a method. `int double(int i) {...}` takes any value of `i`
and does the right thing.

Since Java 5, you can parameterize *classes* on a *type*, where each
different setting of the type results in a distinct class.

You can write:

``` {.java}
class Log<T> {
  private String name;
  private T[] log;
  private int lastIndex = -1;
  ...
```

Each reference to an object's type (originally `String` )is replaced
with the *type parameter*. By convention, as a single upper-case letter.

Now, when you name a type, you can write:

``` {.java}
Log<String> a = new Log<String>();
Log<String> b = new Log<String>();
Log<Integer> c = new Log<Integer>();
```

a and b are both of the same class: `Log<String` (though they are
different objects!). c is of a different class: `Log<Integer>`.

Any time we call a method on one of these objects, the code we get has
the correct type substituted for each `T` in the code.

Note we can't have a `Log<int>` or `Log<char>` etc.; Java's generic
system requires that type parameters be classes.

There are some complications in advanced use of generics, but we will
mostly be able to ignore them in this course.

Exceptions
==========

Nothing ever goes according to plan
-----------------------------------

Earlier, we talked about two potential problems with stacks: underflow
and overflow. Likely you've seen many other problems in your programming
practice: attempting to access elements outside the bounds of an array;
attempting to dereference a null, etc.

One way to deal with these problems is to *guard*, that is, explicitly
check for each possible error before attempting an operation. In older
languages like C, this is what you had to do. Make a mistake, and the
program would crash.

Java gives us an additional capability: when implementing a class, we
can choose to *throw* an *exception* when faced with an abnormal
occurrence.

The caller of the method now can choose to *catch* the exception and
handle it. If it doesn't, it will be thrown to it's caller, and so on.
If no one handles it, the program will exit.

If you want to handle exceptions in your code, you wrap the
possibly-throwing code in a `try { ... }` block, and catch exceptions in
a `catch (TheExceptionClass e) { ... }` block.

``` {.java}
static Reader getFileToRead(BufferedReader input) throws IOException {
  Reader r = null;
  while (r == null) {
    System.out.println("Enter file name: ");
    String fileName = input.readLine();
    try {
      r = new FileReader(fileName);
    } catch (FileNotFoundException e) {
      System.out.println("File not found.");
    }
  }
  return r;
}
  
public static void main(String[] args) throws IOException {
  Reader sysin = new InputStreamReader(System.in);
  BufferedReader input = new BufferedReader(sysin);
  getFileToRead(input);
}
```

Some exceptions are *checked* and some are *unchecked*. The latter are
due to bugs (e.g., NullPointerExceptions) and needn't be caught.

The former are invalid conditions outside the control of the program
(e.g., a database error, or a file can't be found, or the network
connection is broken) and must be handled by the programmer.

Checked exceptions must either be caught, or a method where they might
happen must also throw the exception.

You've seen this (right?) with any file IO: it almost always can raise a
IOException that you must handle.

Programming by contract
=======================

It's annoying to have to validate user input, etc., in all code. It's
easier to assume that inputs are correct.

A formal way of doing so is through *preconditions*, where we explicitly
state that certain things must be true, otherwise we don't guarantee the
performance of the method.

This is part of *programming by contract*.

But: If an error condition isn't covered by the contract, the method
needs to check for it.

The key idea: Isolate this error checking to a single method. For
example, when reading in user input then doing lots of stuff with it,
check the error condition when the input is read. Then all other methods
that operate on it can use a precondition to say they assume the input
is correct.

In Hangman, for example, a good ConsoleGameIO class will do this for
you, and the GameModel can just assume that incoming values are `char`s
that are in the correct format.
