Tues, Mar. 21

Hand out practice midterm, note on Matcher to replace StringTokenizer for pa03 (too late of course for actual pa03 work)

Queues—pg. 227, a “pure” Queue.  Put elements in one end and never get to access them until they come out the other end.

This is known as FIFO.

Operations: enqueue, dequue, getFront--see problem on Practice exam.

Again no size() or iterator, and we can only access the “front” element directly.

Queues are use for dataflow and “workflow”, where tasks flow from one person to another.  In cases like this, different programs or different parts of the same program are doing the enqueue and dequeue operations.  The queue can save objects that are in transit and allow pacing flexibility at both ends.

For a little app, consider the following scenario.  Suppose your program occasionally exhibits bad behavior that becomes apparent at the end of its work.  So you need a log of its activities to help you debug this problem.  But you don’t want a log of all the successful runs.  What to do?

Answer: Queue up the log messages and dump them out only in the troublesome case at the end of the run.

Usage of this service:

      Log myLog = new Log()
     
            myLog.addLine(“about to receive request”);
     
      myLog.addLine(“request received”);
     
      if (big problem)
            myLog.dumpLog();

 

Suppose you have a class PureQueue that provides enqueue, dequeue, and getFront for Strings (as in a practic e exam problem)
 
public class Log
{
      private PureQueue q;
      public Log() { q = new PureQueue();}
      public addLine(String s) { q.enqueue(s); }
      public dumpLog()
      {
          while (!q.isEmpty)
          System.out.println(q.dequeue());
      }
}

Of course, dumpLog() empties the log information, so it can’t be done twice usefully.  That’s why I called it dumpLog instead of printLog, which should be repeatable.  We could implement printLog by re-enqueuing the Strings, after enqueuing a marker, or enqueuing on a differenct queue and switching queues.

Priority Queues

We're not officially covering this now, but just FYI

Priority queues are queues only in a sense.  Real queues are FIFO, but priority queues are highest-priority-out, whenever it came in.

Classic operation names: insert, deleteMin. findMin--see picture on pg. 241.

Recursion: Chap. 7, we’ll cover up to pg. 266, inclusively, after the midterm.  Read 7.2 for next time. Sec. 7.5 and 7.6 are covered in cs310.

We consider the idea of recursive dictionary lookup as described on pg. 252.  We could imagine placing post-its on words we are studying, so we can get back to the original word we looked up.  These post-its can be thought to represent a stack of our recursive search for meaning of words.  They help us back up to where we were before.

Another aspect of recursion is also seen in such dictionary searches.  If word A is defined in terms of word B, and B in terms of C, and C in terms of A, we could end up in an endless cycle if we blindly followed the algorithm.  Similarly, we need to watch out for this when using recursion in programming.

First code example: Fig. 7.1.

Looking for sum up to n:    1 + 2 + 3 +… + (n-1) + n,  

                                       <--see sum to n-1 ->

So we can say:   sum(n) = sum(n-1) + n, a recursive formula.  Also needs a base case: sum(1) = 1 to get started.

We can build a function based on this idea, as shown in Fig. 7.1.  We called the function sum instead of s.

The computation for n=1 is the “base case”, while the computation for n>1 is the recursive case, where case n is expressed in terms of case n-1, making progress towards the base case.

Where’s the stack here?

Answer: we’re using the program execution stack, which has been quietly helping us call functions all along.

Each local variable in a function is housed on the program stack, and when a call is made, the language system starts using a new part of the stack for that newly called function, in effect pushing the old local variables onto the stack.  They are held there, untouched, until execution returns to the original function environment.

Objects themselves are not on the stack, but references to them can be, and often are.

When we display a trace for recursion, we indent for each recursive level, like this:

top level calls sum(4)
         sum(4) calls sum(3)
            sum(3) calls sum(2)
               sum(2) calls sum(1)
                   sum(1) = 1  
When the n=1 call to sum is executing,  n=1 for it, but on the stack, there are values n=2, n=3, and n=4, all saved away for the callers:
[(n=4), (n=3), (n=2), (n=1)]
                                           top, current
 
when sum(1) returns to sum(2), the stack looks like this, with one “activation record” popped off:
[(n=4), (n=3), (n=2)]
                                 top, current
 
Then that level completes the sum(2) calculation, by adding 2 to the 1 returned from sum(1), and so on
 
Example recursive trace: 
top level calls sum(4)
         sum(4) calls sum(3)
            sum(3) calls sum(2)
               sum(2) calls sum(1)
                   sum(1) returns 1
               sum(2) = 1 + 2 = 3
            sum(3) = 3 + 3 = 6
         sum(4) = 6 + 4 = 10
      top level gets 10
 
(Actually, in addition there is a PC (program counter) in each activation record, that tells where to go back to in the code.)
Printing Numbers
 
int i = 12;   This is represented in binary in memory:  12 = 8 + 4, so this is binary 00000…001100, 32 bits in all.
 
When we print this number out, it reaches our screens in ASCII codes:  ASCII ‘1’ = 0x31, ASCII ‘2’ = 0x32, and these are both held in single bytes (i.e. 8 bits).
 
However, it is important to realize that there are three stages in this progression, not just two.
 
First the binary int is turned into a Java String, with two Java chars ‘1’ and ‘2’, then System.out.print() turns the String into two ASCII bytes on the way out.
 
What do the chars look like in the String?
 
They are Unicode characters, each 16 bits long.  This allows Java to handle character sets for many languages, including Asian languages that have thousands of different characters.  Don’t worry, they still have the same order as ASCII: 0, 1, 2, etc, all in a row in the sequence.  In fact all of ASCII is in its same order at the beginning of the Unicode sequence, so everything you might know about ASCII holds for Unicode, except the idea that there are 128 characters.

The fact that the digit characters are all in a row means we can compute the char code from the numeric digit value:

code for '0' = 0x30
code for '1' = 0x30 + 1 = 0x31
code for '2' = 0x30 + 2 = 0x32
...
in general--
    charCode = charCode0 + digitValue.
 
We now consider the first part of this conversion, from int (binary) to String.
 
The basic trick:   12%10 = 2, the last digit
                   and 12/10 = 1, the first digit
 
Try a bigger number:  249
            249%10 = 9, last digit
            249/10 = 24, not a digit, go further:
            24%10 = 4, last digit of 24, middle of 249
            24/10 = 2, top digit
 
We'll continue this after the midterm.