|  | I dette kapitel skal vi studere to fundamentale datastrukturer: stakke 
      og køer. | 
   
    |  |   1. Stakke | 
   
    | LIFO | En stak er en såkaldt LIFO datastruktur. LIFO betyder: 
      "Last In, First Out", og beskriver 
      den bevægelse som data foretager, når det indsættes, og 
      når det udtages. Når man udtager et element fra stakken, vil 
      det være det senest indsatte element. | 
   
    |  | At man kalder denne datastruktor: en stak, stammer fra stak-begrebet i 
      den virkelige verden: | 
   
    |  |  | 
 
    | Push og pop | Når vi indsætter et element i containeren, sker det ved at 
      lægge det ovenpå de elementer, der allerede befinder sig i stakken 
      - vi kalder denne operation: push (dk.: skubbe). Mens det at fjerne 
      et element fra containeren, sker ved at vi tager det der ligger øverst 
      på stakken - vi kalder denne operation: pop (eng.: "pop 
      off": dk.: stikke af, nærmest et slagord) | 
   
    |  |  | 
 
    |  | Man kan implementere en stak på flere forskellige måder, og 
      vi skal i det følgende se på to af disse. Vi vil dog først 
      lave et generelt Stack-interface, 
      der beskriver de operationer som en implementation skal have, uanset hvordan 
      den ellers er realiseret: | 
 
    |  |  
        
           
            | 
                 
                  | Stack.java |   
                  | public interface Stack {
  
  public void push( Object obj );
  public Object pop();
  public Object peek();
  
  public int size();
  public boolean empty();
  public boolean full();
  public void clear();
} |  |  | 
 
    |  | Vi genkender her push, 
      og pop, der er containerens 
      indsæt- og udtag-metoder. Vi har også medtaget en metode: peek, 
      der ofte er nyttig. peek-metoden 
      returnerer det øverste element fra stakken, uden at fjerne 
      det. Dernæst følger en række simple standard-metoder, 
      der findes i næsten alle containere. | 
   
    |  |   1.1 Implementeret med array | 
   
    |  | Anvender man et array, til at implementere en stak, gøres det på 
      følgende måde: | 
   
    |  |  
        
           
            | 
                 
                  | ArrayStack.java |   
                  | public class ArrayStack implements Stack {
  private Object[] stack;
  private int size;
  
  public ArrayStack() {
    this( 10 );
  }
  
  public ArrayStack( int length ) {
    stack = new Object[ length ];
    clear();
  }
  
  public void push( Object obj ) {
    if ( !full() ) {
      stack[size] = obj;
      size++;
    } else
      throw new StackOverflowError();
  }
  
  public Object pop() {
    if ( !empty() ) {
      size--;
      return stack[size];
    } else
      return null;
  }
  
  public Object peek() {
    if ( !empty() )
      return stack[size-1];
    else
      return null;
  }
  
  public int size() {
    return size;
  }
  
  public boolean empty() {
    return size == 0;
  }
  
  public boolean full() {
    return size == stack.length;
  }
  
  public void clear() {
    size = 0;
  }
} |  |  | 
 
    |  | Man skal her særligt lægge mærke til problematikken 
      omkring array'ets statiske længde. I forbindelse med pop 
      og peek kan vi blot 
      returne null, som 
      en indikation af, at der ikke var noget element at pop'e 
      fra stakken. Med push 
      er det straks mere ubekvemt. Det er en mulighed at lade metoden returnere 
      boolsk om operationen lykkedes. Alternativt kan man bruge en exception som 
      vi har valgt her. | 
   
    | Overflow | Mere præcist bruger vi et Error, 
      idet vi kaster et StackOverflowError, 
      hvis stakken er fuld i forvejen. Valget af StackOverflowError 
      skyldes primært, at den findes i forvejen. StackOverflowError 
      kastes normalt kun når stakken i den virtuelle maskine løber 
      fuld. Man kaldet det generelt et overflow, når man vil lægge 
      et element på en stak, der allerede er fuld - den flyder over! | 
   
    |  |   1.2 Implementeret med liste | 
   
    |  | Idéen med at implementere en stak vha. en liste, er at en liste 
      aldrig løber fuld. Samtidig fylder den ikke mere, end der er elementer 
      i stakken, da dens størrelse ændrer sig dynamisk (dog vil hvert 
      element til gengæld fylde mere end i et array). | 
   
    | Adapter | Når vi implementerer Stack-interfacet, 
      gør vi det ved at lave en klasse, der anvender en liste som container. 
      Det betyder, at den fungerer som Adapter (se evt. Adapter 
      Pattern) | 
 
    |  | Lad os se implementationen af klassen: ListStack: | 
 
    |  |  
        
           
            | 
                 
                  | ListStack.java |   
                  | import java.util.LinkedList;
public class ListStack implements Stack {
  private LinkedList stack;
  
  public ListStack() {
    stack = new LinkedList();
    clear();
  }
  
  public void push( Object obj ) {
    stack.addFirst( obj );
  }
  
  public Object pop() {
    return stack.removeFirst();
  }
  
  public Object peek() {
    return stack.getFirst();
  }
  
  public int size() {
    return stack.size();
  }
  
  public boolean empty() {
    return stack.isEmpty();
  }
  
  public boolean full() {
    return false;
  }
  
  public void clear() {
    stack.clear();
  }
} |  |  | 
 
    |  | Man bemærker at implementationen er uhyre enkel, idet vi med en 
      liste undgår de fejl-situationer, som skulle tages i betragtning i 
      forbindelse med et array. | 
   
    |  | Man bemærker, at full-metoden 
      ikke er specielt relevant for en liste, men at vi implementerer den for 
      at holde interfacet generelt (og fordi vi skal). | 
   
    |  |   2. Køer | 
   
    | FIFO | En kø er en såkaldt FIFO datastruktur. FIFO 
      betyder: "First In, First Out", og 
      beskriver den bevægelse som data foretager, når det indsættes, 
      og når det udtages. Når man udtager et element fra køen, 
      vil det altid være det "ældste" element, dvs. det 
      element, der har befundet sig længst tid i køen - det element 
      der står forrest! | 
   
    |  |  | 
 
    |  | I lighed med implementationen af en stak, vil vi også her erklære 
      et interface, og implementere en kø vha. henholdvis et array og en 
      liste: | 
 
    |  |  
        
           
            | 
                 
                  | Queue.java |   
                  | interface Queue {
  
  public void insert( Object obj );
  public Object remove();
  public Object peek();
  
  public int size();
  public boolean empty();
  public boolean full();
  public void clear();
} |  |  | 
 
    |  | I forbindelse med en kø, kalder vi de to indsæt- og udtag-metoder: 
       insert og remove. | 
   
    |  |   2.1 Implementeret med array | 
   
    |  | At implementere en kø i et array kræver en del omtanke. | 
   
    |  | I forbindelse med en kø, vil vi ikke alene have en tæller: 
       size, der fortæller 
      hvor mange elementer der er i køen. Vi skal også have to index: 
       first og last, 
      der fortæller hvor køen starter, henholdsvis hvor den slutter: | 
 
    |  |  | 
 
    | X | I figuren markerer krydserne elementer i køen. | 
 
    | Begge går mod højre | En kø vil ikke altid starte ved index 0, 
      da first vil bevæge 
      sig mod højre, efterhånden som vi fjerner elementer fra køen. 
       last vil ligeledes 
      bevæge sig mod højre, når vi indsætter elementer 
      i køen. Bemærk, at vi vælger at lade last 
      udpege den næste ledig plads i køen - ikke det 
      sidste element i køen, mens first 
      udpeger det forrest element. | 
 
    |  | Der skal ikke meget fantasi til at forestille sig, at vi får et 
      problem når køen når ud i enden af array'et. Eftersom 
      der løbende opstår ledige pladser fra starten af array'et og 
      fremefter, efterhånden som vi udtager elementer og first 
      flytter sig mod højre, er løsningen naturligvis at genbruge 
      disse pladser. | 
 
    | Modulus | For at kunne gøre dette, må vi flytte last 
      tilbage til index 0, 
      når den går ud over kanten af array'et. Det gøres bekvemt 
      med modulus, 
      idet vi anvender formlen: | 
 
    |  | last = (last+1) % 
        længden | 
 
    |  | På et tidspunkt vil også first 
      ryge ud over kanten, og da first 
      på samme måde bevæger sig mod højre, kan vi anvende 
      den samme formel for begge index: | 
 
    |  | first 
        = (first+1) % længden | 
 
    |  | Hvis vi rent visuelt bøjer array'et rund, så det danner en 
      cirkel, kan vi se hvilken effekt dette får: | 
 
    |  |  | 
 
    | Cirkulært array | Ved at bruge modulus, i forbindelse med inkrementeringen af index, opnår 
      vi at array'et kommer til virke som et cirkulært array. I figuren 
      til venstre, har vi markeret det sted hvor de to ender af array'et er sat 
      sammen, med en stiplet linie. | 
 
    |  | Implementationen af en kø med et array bliver som følger: | 
 
    |  |  
        
           
            | 
                 
                  | ArrayQueue.java |   
                  | public class ArrayQueue implements Queue {
  private Object[] queue;
  private int first, last;
  private int size;
  
  public ArrayQueue() {
    this( 10 );
  }
  
  public ArrayQueue( int length ) {
    queue = new Object[ length ];
    clear();
  }
  public void insert( Object obj ) {
    if ( !full() ) {
      queue[ last ] = obj;
      last = (last+1) % queue.length;
      size++;
    } else
      throw new StackOverflowError();
  }
  
  public Object remove() {
    if ( !empty() ) {
      Object res = queue[ first ];
      first = (first+1) % queue.length;
      size--;
      return res;
    } else
      return null;
  }
  
  public Object peek() {
    if ( !empty() )
      return queue[ first ];
    else
      return null;
  }
  
  public int size() {
    return size;
  }
  
  public boolean empty() {
    return size == 0;
  }
  
  public boolean full() {
    return size == queue.length;
  }
  
  public void clear() {
    first = last = 0;
    size = 0;
  }
} |  |  | 
 
    | size 
      nødvendig | Bemærk, at det tomme array har både first 
      og last sat til 0. 
      For at kunne skelne mellem de to situationer, hvor køen er henholdsvis 
      tom og fuld anvender vi size, 
      da first vil være 
      lig last i begge situationer, 
      og dette kriterium derfor ikke kan anvendes. | 
   
    |  |   2.2 Implementeret med liste | 
   
    |  | Pga. af dynamikken i en hægtet liste, bliver implementation af 
      en kø meget enkel: | 
   
    |  |  
        
           
            | 
                 
                  | ListQueue.java |   
                  | import java.util.LinkedList;
public class ListQueue implements Queue {
  private LinkedList queue;
  
  public ListQueue() {
    queue = new LinkedList();
  }
  
  public void insert( Object obj ) {
    queue.addLast( obj );
  }
  
  public Object remove() {
    return queue.removeFirst();
  }
  
  public Object peek() {
    return queue.getFirst();
  }
  
  public int size() {
    return queue.size();
  }
  
  public boolean empty() {
    return queue.isEmpty();
  }
  
  public boolean full() {
    return false;
  }
  
  public void clear() {
    queue.clear();
  }
} |  |  | 
 
    |  | Vi vælger her at indsætte i slutningen af listen og udtage 
      i starten. Man kunne naturligvis lige så vel have valgt at gøre 
      det omvendt. | 
   
    |  |   3. Eksempler | 
   
    |  |   3.1 Postfix notation | 
   
    |  | (Med mindre andet er nævnt, antages det i det følgende, 
      at alle operander er på ét ciffer) | 
   
    |  | Vi er i almindelighed vant til at skrive regneudtryk som f.eks.: | 
   
    |  | 5 + 3 * 8 | 
   
    | Mellem | hvor vi placerer operatorerne mellem operanderne. | 
   
    | Præcedens | Samtidig har vi præcedens-regler, der f.eks. bestemmer at multiplikation 
      skal udføres før addition; hvorved resultatet af ovenstående 
      bliver 29, og ikke 64, som man ville få; hvis man evaluerede fra venstre 
      mod højre uden skelen til præcedens. | 
   
    |  | Hvis vi ønskede at resultatet skulle være 64, og dermed, 
      at 5+3 skulle evalueres først, måtte vi ty til paranteser for 
      at gennemtvinge en anden evalueringsrækkefølge: | 
   
    |  | ( 5 + 3 ) * 8 | 
   
    |  | Selvom vi betragter denne notationsform som en selvfølge, er det 
      ikke en nødvendighed. I virkeligheden findes der en mere enkelt notation, 
      som eliminerer behovet for såvel præcedens som paranteser - 
      den kaldes postfix notation. | 
   
    | Omvendt polsk notation | Den notation som vi almindeligvis bruger, kaldes infix notation, 
      fordi vi placerer operatorerne mellem (in) operanderne. På samme måde 
      betyder postfix notation, at vi placerer operatorerne efter (port) 
      operanderne. Der skal ikke meget fantasi til at forestille sig, at der også 
      findes noget, der hedder prefix notation; hvor operatorerne placerer 
      før (pre) operanderne. Prefix notation blev opfundet af en polsk 
      matematiker: J. Lukasiewicz, og pudsigt nok, kalder man derfor også 
      postfix notation for: omvendt polsk notation - fordi det er 
      det omvendte af, hvad Lukasiewicz fandt på! | 
   
    |  |   3.1.1 Evaluering af postfix | 
   
    |  | Inden vi ser hvordan man kan oversætte et udtryk fra infix notation 
      til postfix notation, vil vi se hvorfor de er så fordelagtige. Som 
      nævnt er fordelen ved postfix notation, at man ikke har behov for 
      præcedensregler og paranteser. | 
   
    |  | Udtrykket: ( 5 + 3 ) * 8, skrives på følgende måde 
      i postfix notation: | 
   
    |  | 5 3 + 8 *  | 
   
    | Bruger en stak | Udtryk skrevet i postfix notation evalueres fra venstre mod højre, 
      vha. en stak. Idéen med stakken er at man push'er 
      operanderne efterhånden som man møder dem, idet man læser 
      fra venstre mod højre. Hver gang man møder en operator, pop'er 
      man de to øverste elementer fra stakken, udfører operationen 
      på dem, og push'er 
      resultatet tilbage på stakken. Bemærk, at det første 
      element man pop'er 
      skal være højre operand, mens det andet skal stå på 
      venstre side af operatoren. Denne operand-rækkefølge svarer 
      til, at man lægger hovedet på skrå mod venstre, når 
      man ser på elementerne på stakken. Dette skyldes, at man ved 
      at lægge hovedet på skrå mod venstre, vil se operanderne 
      i den rækkefølge de stod i den oprindelige postfix notation. | 
   
    |  | Lad os evaluere ovenstående udtryk: | 
   
    |  | Først har vi udgangspunktet: Udtrykket står klar i postfix 
      notation, og vi har en tom stak: | 
 
    |  |  | 
 
    |  | Vi ser, at de to første elementer i udtrykket er operander, og 
       push'er dem en efter 
      en på stakken: | 
   
    |  |  | 
 
    |  | Dernæst møder vi en operator: +, 
      som vi lader virke på de to øverste elementer fra stakken. 
      Vi push'er resultatet: 
      8, tilbage på stakken: | 
   
    |  |  | 
 
    |  | Det næste element er en operand: 8, 
      som vi push'er på 
      stakken: | 
   
    |  |  | 
 
    |  | Endelig er der det sidste element i udtrykket: operatoren *, 
      som vi lader virke på de to øverste elementer fra stakken. 
      Vi push'er resultatet: 
      64, tilbage på stakken: | 
   
    |  |  | 
 
    |  | Vi har nu udtømt udtrykket, og resultatet: 64, kan pop'es 
      fra stakken. | 
   
    |  | Som man kan se, er metoden meget enkel, og den egner sig derfor godt som 
      algoritme til en computer. | 
   
    |  |  | 
   
    | Øvelse: | Evaluer udtrykket: | 
   
    |  |  
        5 8 3 / - | 
   
    |  | Vær særlig opmærksom på operandernes rækkefølge, 
      når du bruger operatorerne: / 
      og -. | 
   
    |  | Resultatet skal give 3. | 
   
    |  |   3.1.2 Fra infix til postfix | 
   
    |  | At oversætte fra infix til postfix notation, er ikke så enkelt 
      som at evaluere postfix notation - det kræver et større "apparat". | 
   
    | Tanenbaum | Jeg lærte selv at konvertere fra infix til postfix notation vha. 
      Andrew S. Tanenbaums: "Sctructured Computer Organisation, 2nd. ed.", 
      s. 221-27; hvor han gennemgår en algoritme der stammer fra Dijkstra. 
      I forbindelse med beskrivelsen af denne algoritme bruger Tanenbaum en illustration 
      med en jernbane mellem New York og Californien, der går via Texas. 
      Jeg vil i min gennemgang bruge en mere lokal jernbane, så vi skal 
      ud og køre med tog fra Silkeborg til Herning, via Ikast: | 
 
    | Jernbane |  | 
 
    |  | Pilene viser de mulige retning vi kan køre i, og man bemærker, 
      at der er et sporskifte på turen fra Silkeborg mod vest. | 
   
    | Infix i Silkeborg | Man starter med at placere udtrykket i dets infix notation, i Silkeborg. 
      Man opdeler udtrykket i sine grundelementer: operatorer og operander (i 
      det følgende betegnet som tokens), således at det første 
      token står forrest; hvilket også er den naturlige orientering 
      sådan som tegningen bevidst er lavet. | 
   
    |  | Hvis vi f.eks. ønskede at oversætte udtrykket ovenfor, til 
      postfix notation, ville vi starte med: | 
   
    | Starten |  | 
 
    |  | Disse syv vogne skal nu sendes af sted fra Silkeborg, en efter en. Vi 
      placerer også to "stopklodser" i henholdsvis Silkeborg og 
      Ikast - markeret med et T, 
      for Terminator. | 
   
    | Køer og stak | Det er væsentligt at bemærke, at vi vil bruge køer 
      i henholdsvis Silkeborg og Herning, mens vi vil bruge en stak i Ikast. Det 
      betyder at vognene i Ikast vil forlade stationen "last in, first out", 
      når de fortsætter mod Herning. | 
   
    |  | For operander gælder der, at de altid kører direkte fra Silkeborg 
      til Herning. Der vil derfor kun komme operatorer til Ikast. | 
   
    |  | Reglerne for hvordan operatorerne flyttes, kan opstilles i en tabel | 
   
    | Flytte-regler |  
        
           
            |  | T | + | - | * | / | ( | ) |   
            | T | 4 | 1 | 1 | 1 | 1 | 1 | 5 |   
            | + | 2 | 2 | 2 | 1 | 1 | 1 | 2 |   
            | - | 2 | 2 | 2 | 1 | 1 | 1 | 2 |   
            | * | 2 | 2 | 2 | 2 | 2 | 1 | 2 |   
            | / | 2 | 2 | 2 | 2 | 2 | 1 | 2 |   
            | ( | 5 | 1 | 1 | 1 | 1 | 1 | 3 |  | 
   
    |  | Tabellen bruges på den måde at man ser hvilke to operatorer 
      der står i Silkeborg og Ikast. Dernæst slår man op i tabellen, 
      idet Silkeborg er vandret og Ikast er lodret. | 
   
    |  | Tallene i tabellen står for følgende handlinger, man dernæst 
      udfører: | 
   
    | Handlinger |  
        
           
            | Nr. | Handling |   
            | 1 | En vogn kører fra Silkeborg til Ikast |   
            | 2 | En vogn kører fra Ikast til Herning |   
            | 3 | Der forsvinder en vogn i både Ikast og Silkeborg |   
            | 4 | Terminer - Resultatet står i Herning |   
            | 5 | Fejl |  | 
   
    | Postfix i Herning | Når man når til at udføre handling nr. 4, står 
      resultatet, i postfix notation, i Herning. | 
   
    |  |  | 
   
    |  | Lad os se hvordan dette forløber i forbindelse med eksemplet ovenfor. | 
   
    | Starten, med alle vogne i Silkenorg |  | 
 
    | 1-2: Venstre-parantes til Ikast, og operand direkte 
      til Herning |  | 
 
    | 3-4: Operator til Ikast (se tabellen), og operand direkte 
      til Herning |  | 
 
    | 5: Operator fra Ikast til Herning (se tabellen) |  | 
 
    | 6: De to paranteser forsvinder (se tabellen) |  | 
 
    | 7-8: Operator til Ikast (se tabellen), og operand direkte 
      til Herning |  | 
 
    | 9: Operator fra Ikast til Herning (se tabellen) |  | 
 
    |  | Og dermed har vi udtrykket stående i Herning, i den ønskede 
      postfix notation. | 
   
    |  |   3.1.3 Implementation | 
   
    |  | I forbindelse med implementationen af konvertering af infix til postfix 
      notation, samt evaluering af postfix notation, er den største udfordring 
      at håndtere operatorer og operander. De er typemæssigt forskellige, 
      og et infix/postfix udtryk er derfor en heterogen liste af elementer. Vi 
      vælger at lave en fælles superklasse til disse, som vi kalder 
       Token: | 
 
    | Token-klasser |  | 
 
    |  | Vi har i samme åndedrag indført en Terminator-klasse, 
      til at repræsentere terminatorerne i henholdsvis Silkeborg og Ikast. | 
   
    |  | Det eneste vi placerer i Token-klassen 
      er to fabriks-metoder, der konstruerer en operand/operator alt efter hvad 
      der syntaktisk er grundlag for: | 
   
    |  |  
        
           
            | 
                 
                  | Token.java |   
                  | public abstract class Token {
  public static Token createToken( char c ) {
    switch ( c ) {
      case '+':
        return new Plus();
      case '-':
        return new Minus();
      case '*':
        return new Gange();
      case '/':
        return new Divider();
      case '(':
        return new VenstreParantes();
      case ')':
        return new HojreParantes();
        
      default:
        return new Operand( "" + c );
    }
  }
  
  public static Token createToken( String s ) {
    if ( s.length() == 1 )
      return createToken( s.charAt(0) );
    else
      return new Operand( s );
  }
} |  |  | 
 
    |  | Den sidste af fabriks-metoderne anvendes i forbindelse med evalueringen 
      af udtryk i postfix notation, da der i den forbindelse kan optræde 
      operander med mere end et ciffer. | 
   
    |  | Dernæst har vi Operand-klassen: | 
   
    |  |  
        
           
            | 
                 
                  | Operand.java |   
                  | public class Operand extends Token {
  private int operand;
  
  public Operand( String s ) {
    this( Integer.parseInt( s ) );
  }
  
  public Operand( int operand ) {
    this.operand = operand;
  }
  
  public int getValue() {
    return operand;
  }
  
  public String toString() {
    return "" + operand;
  }
} |  |  | 
 
    |  | Operator-klasserne 
      har en fælles abstrakt super-klasse: | 
   
    |  | Og Operator-klassen: | 
   
    |  |  
        
           
            | 
                 
                  | Operator.java |   
                  | public abstract class Operator extends Token {
  
  public Operand evaluate( Operand op1, Operand op2 ) {
    return new Operand( evaluateHook( op2.getValue(), op1.getValue() ) );
  }
  public abstract int evaluateHook( int op1, int op2 );
} |  |  | 
 
    | Template Method | Her anvendes Template 
      Method for at gøre livet lettere for de mange subklasser. 
      Template-metoden får fat i de to integers, og laver den nye 
      instans af Operand, 
      der skal returneres. | 
   
    |  | Bemærk, at evaluate-metoden 
      bytter om på operanderne. Det gør den, da den første 
      parameter vil være det øverste element fra stakken (se senere). | 
   
    |  | Subklasserne kan laves på samlebånd: | 
   
    |  |  
        
           
            | 
                 
                  | Plus.java |   
                  | public class Plus extends Operator {
  
  public int evaluateHook( int v1, int v2 ) {
    return v1 + v2;
  }
  public String toString() {
    return "+";
  }
} |  |  | 
 
    |  |  
        
           
            | 
                 
                  | Minus.java |   
                  | public class Minus extends Operator {
  
  public int evaluateHook( int v1, int v2 ) {
    return v1 - v2;
  }
  public String toString() {
    return "-";
  }
} |  |  | 
 
    |  |  
        
           
            | 
                 
                  | Gange.java |   
                  | public class Gange extends Operator {
  
  public int evaluateHook( int v1, int v2 ) {
    return v1 * v2;
  }
  public String toString() {
    return "*";
  }
} |  |  | 
 
    |  |  
        
           
            | 
                 
                  | Divider.java |   
                  | public class Divider extends Operator {
  
  public int evaluateHook( int v1, int v2 ) {
    return v1 / v2;
  }
  public String toString() {
    return "/";
  }
} |  |  | 
 
    |  |  
        
           
            | 
                 
                  | VenstreParantes.java |   
                  | public class VenstreParantes extends Operator {
  
  public int evaluateHook( int v1, int v2 ) {
    throw new NoSuchMethodError();
  }
  public String toString() {
    return "(";
  }
} |  |  | 
 
    |  |  
        
           
            | 
                 
                  | HojreParantes.java |   
                  | public class HojreParantes extends Operator {
  
  public int evaluateHook( int v1, int v2 ) {
    throw new NoSuchMethodError();
  }
  public String toString() {
    return ")";
  }
} |  |  | 
 
    |  | Det eneste, der er værd at bemærke, er implementationen af 
      hook-metoden i: VenstreParantes 
      og HojreParantes, 
      da disse ikke giver mening i relation til paranteser. Såfremt de skulle 
      blive kaldt, kaster de en NoSuchMethodError. | 
   
    |  | Endelig har vi de to metoder: infixToPostfix 
      og evaluatePostfix, 
      og en testanvendelse: | 
   
    |  |  
        
           
            | 
                 
                  | Main.java |   
                  | import java.text.*;
public class Main {
                                   //<T> +  -  *  /  (  )
  private static int[][] action = { { 4, 1, 1, 1, 1, 1, 5 },   //<T>
                                    { 2, 2, 2, 1, 1, 1, 2 },   // +
                                    { 2, 2, 2, 1, 1, 1, 2 },   // -
                                    { 2, 2, 2, 2, 2, 1, 2 },   // *
                                    { 2, 2, 2, 2, 2, 1, 2 },   // /
                                    { 5, 1, 1, 1, 1, 1, 3 } }; // (
  /*
   * 1. En vogn går fra Silkeborg til Ikast
   * 2. En vogn går fra Ikast til Herning
   * 3. Der forsvinder både en vogn i Ikast og Silkeborg
   * 4. Terminer - resultatet står i Herning
   * 5. Fejl
   */
  
  private static String infixToPostfix( String infix ) {
    ListQueue silkeborg = new ListQueue();
    ListStack ikast     = new ListStack();
    ListQueue herning   = new ListQueue();
    /*
     * Gøre vognene klar i Silkeborg
     */
    StringCharacterIterator it = new StringCharacterIterator( infix );
    
    it.first();
    do {
      silkeborg.insert( Token.createToken( it.current() ) );
    } while ( it.next() != CharacterIterator.DONE );
    
    silkeborg.insert( new Terminator() );
    
    /*
     * Gøre klar i Ikast
     */
    ikast.push( new Terminator() );
    
    /*
     * Turen går fra Silkeborg til Herning over Ikast
     */
    loop:
      while ( true ) {
        if ( !(silkeborg.peek() instanceof Operand) ) {
          
          switch ( action[ tokenToIndex( (Token) ikast.peek() ) ]
                         [ tokenToIndex( (Token) silkeborg.peek() ) ] ) {
        
            case 1: // En vogn går fra Silkeborg til Ikast
                    ikast.push( silkeborg.remove() );
                    break;
                  
            case 2: // En vogn går fra Ikast til Herning
                    herning.insert( ikast.pop() );
                    break;
                  
            case 3: // Der forsvinder både en vogn i Ikast og Silkeborg
                    ikast.pop();
                    silkeborg.remove();
                    break;
                  
            case 4: // Terminer - resultatet står i Herning
                    break loop;
                  
            case 5: // Fejl
                    System.out.println( "Error" );
                    break;
          }
        } else
          herning.insert( silkeborg.remove() );
      }
    
    StringBuffer sb = new StringBuffer();
    
    while ( !herning.empty() )
      sb.append( herning.remove().toString() );
    
    return sb.toString();
  }
  
  private static int tokenToIndex( Token token ) {
    if ( token instanceof Terminator )
      return 0;
    else if ( token instanceof Operator ) {
      Operator operator = (Operator) token;
      if ( operator.equals( '+' ) )
        return 1;
      else if ( operator.equals( '-' ) )
        return 2;
      else if ( operator.equals( '*' ) )
        return 3;
      else if ( operator.equals( '/' ) )
        return 4;
      else if ( operator.equals( '(' ) )
        return 5;
      else if ( operator.equals( ')' ) )
        return 6;
      else
        return -1;
    } else
      return -1;
  }
  
  private static int evaluatePostfix( String postfix ) {
    ListStack stack = new ListStack();
    ListQueue expression = new ListQueue();
    
    /*
     * Gøre udtrykket klar
     */
    StringCharacterIterator it = new StringCharacterIterator( postfix );
    
    it.first();
    do {
      expression.insert( Token.createToken( it.current() ) );
    } while ( it.next() != CharacterIterator.DONE );
    
    /*
     * Evaluer udtryk
     */
    while ( !expression.empty() ) {
      Token token = (Token) expression.remove();
      
      if ( token instanceof Operator ) {
        Operator operator = (Operator) token;
        stack.push( operator.evaluate( (Operand) stack.pop(),
                                       (Operand) stack.pop() ) );
      } else
        stack.push( token );
    }
    
    return ((Operand) stack.pop()).getValue();
  }
  
  public static void main( String[] argv ) {
    String infix = "5-8/3";
    
    String postfix = infixToPostfix( infix );
    int result = evaluatePostfix( postfix );
    
    System.out.println( infix + " = " + postfix + " = " + result );
  }
} |  |   
            |  |  | 
 
    |  |  |