59
1 Anvendelser I Leg og spil

Anvendelser I

Embed Size (px)

DESCRIPTION

Leg og spil. Anvendelser I. Plan. Leg og spil OrdkvadraterTomandsspil (Kryds-og-bolle) Stakke og oversættere Check af parentesstrukturerEn simpel kalkulatorIndlæsning og beregning af aritmetiske udtryk. Ordkvadrater. - PowerPoint PPT Presentation

Citation preview

Page 1: Anvendelser I

1

Anvendelser I

Leg og spil

Page 2: Anvendelser I

2

• Leg og spilOrdkvadraterTomandsspil (Kryds-og-bolle)

• Stakke og oversættereCheck af parentesstrukturerEn simpel kalkulatorIndlæsning og beregning af aritmetiske udtryk

Plan

Page 3: Anvendelser I

3

Ordkvadrater

Problem: Givet et kvadrat bestående af bogstaver samt en ordbog. Find alle de ord i kvadratet, der optræder i ordbogen.

Ordene læses vandret, lodret eller diagonalt i enhver retning (i alt 8 retninger).

Eksempler:

tank, ank, an, en, real, te, anke, nota, ral, ro, et, at, ark, ko, kok

Page 4: Anvendelser I

4

Ineffektiv algoritme:

for ethvert ord W i ordbogen for enhver række R for enhver søjle C

for enhver retning D afgør om W findes i række R, søjle C og retning D

Løsningsalgoritmer

Antag R = C = 32 og W = 40,000 Antal ordsammenligninger i indre løkke: 40,000*R*C*8 = 327,680,000

Page 5: Anvendelser I

5

Forbedret algoritme:

for enhver række R for enhver søjle C for enhver retning D for enhver ordlængde L afgør om de L tegn i række R, søjle C og retning D

findes som et ord i ordbogen

Antal check i indre løkke (antag Lmax = 20): R*C*8*L = 32*32*8*20 = 163,840

Hvis hvert opslag i ordbogen foretages med binær søgning, foretages maksimalt 163,840 * (log240,000+ 1) = 163,840 * 16 = 2,621,440 ordsammenligninger.

For eksemplet her er algoritmen blevet cirka 125 gange hurtigere.

Page 6: Anvendelser I

6

Yderligere forbedret algoritme:

for enhver række R for enhver søjle C for enhver retning D for enhver ordlængde L afgør om de L tegn i række R, søjle C og retning D

findes som et ord i ordbogen hvis de ikke udgør et præfiks for noget ord i ordbogen, så break; // ud af den inderste løkke

Om de L tegn udgør et præfiks for et ord i ordbogen kan afgøres ved binær søgning.

Page 7: Anvendelser I

7

Implementering i Java

int solvePuzzle() { int matches = 0;

for (int r = 0; r < rows; r++) for (int c = 0; c < columns; c++) for (int rd = -1; rd <= 1; rd++ ) for (int cd = -1; cd <= 1; cd++) if (rd != 0 || cd != 0 ) matches += solveDirection(r, c, rd, cd);

return matches;}

Page 8: Anvendelser I

8

int solveDirection(int r, int c, int rd, int cd) { int numMatches = 0; String chars = "" + theBoard[r][c];

for (int i = r + rd, j = c + cd; i >= 0 && j >= 0 && i < rows && j < columns; i += rd, j += cd) { chars += theBoard[i][j]; int index = prefixSearch(theWords, chars); if (!theWords[index].startsWith(chars)) break; if (theWords[index].equals(chars)) { numMatches++; System.out.println("Found " + chars + " at " + r + " " + c + " to " + i + " " + j ); } } return numMatches;}

Page 9: Anvendelser I

9

int prefixSearch(String[] a, String chars) { int low = 0; int high = a.length - 1;

while (low < high) { int mid = (low + high) / 2; if (a[mid].compareTo(chars) < 0) low = mid + 1; else high = mid; } return low;}

int prefixSearch(String[] a, String chars) { int idx = Arrays.binarySearch(chars); return idx >= 0 ? idx : -(idx + 1);}

eller

Page 10: Anvendelser I

10

Spil

Page 11: Anvendelser I

11

Kryds-og-bolle (Engelsk: Tic-tac-toe)

...

...

............

Bolle vinder Uafgjort Kryds vinder

Page 12: Anvendelser I

12

public class TicTacToe { public static final int HUMAN = 0;

public static final int COMPUTER = 1;public static final int EMPTY = 2;

public static final int HUMAN_WIN = 0;public static final int DRAW = 1;public static final int UNCLEAR = 2;public static final int COMPUTER_WIN = 3;

public TicTacToe() { clearBoard( ); }

public Best chooseMove(int side) { ... }public boolean playMove(int side, int row, int column) { ... }public void clearBoard() { ... }

public boolean boardIsFull() { ... } boolean isAWin(int side) { ... }

private int[][] board = new int[3][3]; private void place(int row, int column, int piece) { ... } private boolean squareIsEmpty(int row, int column) { ... } private int positionValue() { ... } }

Page 13: Anvendelser I

13

class Best { int row, column; int val;

public Best(int v, int r, int c) { val = v; row = r; column = c; }

public Best(int v) { this(v, 0, 0); }}

Page 14: Anvendelser I

14

Minimax strategien

1. Hvis stillingen er en slutstilling, så returner dens værdi.

2. Ellers, hvis det er computeren (Max) til at trække,så returner den maksimale værdi af alle de stillinger, der fremkommer ved at udføre et træk. Værdierne beregnes rekursivt.

3. Ellers, hvis det er mennesket (Min) til at trække,så returner den minimale værdi af alle de stillinger, der fremkommer ved at udføre et træk.

Værdierne beregnes rekursivt.

Page 15: Anvendelser I

15

public Best chooseMove(int side) { int bestRow = 0, bestColumn = 0; int value, opp;

if ((value = positionValue()) != UNCLEAR) return new Best(value); if (side == COMPUTER) { opp = HUMAN; value = HUMAN_WIN; } else { opp = COMPUTER; value = COMPUTER_WIN; } for (int row = 0; row < 3; row++) for (int column = 0; column < 3; column++) if (squareIsEmpty(row, column)) { place(row, column, side); Best reply = chooseMove(opp); place(row, column, EMPTY); if (side == COMPUTER && reply.val > value || side == HUMAN && reply.val < value) { value = reply.val; bestRow = row; bestColumn = column; } } return new Best(value, bestRow, bestColumn);}

Page 16: Anvendelser I

16

Minimax strategien udfører megen overflødig søgning

C1 C2 C3

H2A H2B H2DH2C

uafgjort

uafgjort Beskæring: C2 kan aldrig blive bedre end “uafgjort”.

maksimer

minimer

Page 17: Anvendelser I

17

Alpha-beta-beskæring

Trækket H2A kaldes en gendrivelse af trækket C2.

Der er et træk (C1), der er lige så godt - eller bedre.

alpha: Den hidtil bedste værdi opnået af computeren. beta: Den hidtil bedste værdi opnået af mennesket.

Beskæring sker

(1) hvis mennesket opnår en værdi, der er mindre end eller lig med alpha.

(2) hvis computeren opnår en værdi, der er større endeller lig med beta.

Page 18: Anvendelser I

18

public Best chooseMove(int side, int alpha, int beta) { int bestRow = 0, bestColumn = 0; int value, opp; if ((value = positionValue()) != UNCLEAR) return new Best(value); if (side == COMPUTER) { opp = HUMAN; value = alpha; } else { opp = COMPUTER; value = beta; }Outer: for (int row = 0; row < 3; row++) for (int column = 0; column < 3; column++) if (squareIsEmpty(row, column)) { place(row, column, side); Best reply = chooseMove(opp, alpha, beta); place(row, column, EMPTY); if (side == COMPUTER && reply.val > value || side == HUMAN && reply.val < value) { value = reply.val; if (side == COMPUTER) alpha = value; else beta = value; bestRow = row; bestColumn = column; if (alpha >= beta) break Outer; } } return new Best(value, bestRow, bestColumn);}

Page 19: Anvendelser I

19

Best chooseMove(int side) { return chooseMove(side, HUMAN_WIN, COMPUTER_WIN);}

Driver-rutine

Page 20: Anvendelser I

20

Effekten af alpha-beta-beskæring

I praksis er antallet af knuder, der bliver undersøgt ved brug af alpha-beta-beskæring O( ), hvor N er det antal knuder, der ville blive undersøgt uden brug af alpha-beta-beskæring.

N

Beskæringen er størst, når algoritmen i enhver stilling altid undersøger det bedste træk først.

På den samme tid kan der søges dobbelt så dybt.

Page 21: Anvendelser I

21

Beskæring ved hjælp af tabel

Undgå genberegninger ved at gemme evaluerede stillinger i en tabel.

To søgninger resulterer i samme stilling.

Benyt en transpositionstabel i form af en hashtabel (HashMap).

Page 22: Anvendelser I

22

class Position { int[][] board; int value;

Position(int theBoard[][]) { board = new int[3][3]; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) board[i][j] = theBoard[i][j]; }

public boolean equals(Object rhs) { for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) if (board[i][j] != ((Position) rhs).board[i][j]) return false; return true; }

public int hashCode() { int hashVal = 0; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) hashVal = hashVal * 4 + board[i][j]; return hashVal; }}

Page 23: Anvendelser I

23

public Best chooseMove(int side, int alpha, int beta, int depth) { int bestRow = 0, bestColumn = 0; int value, opp; Position thisPosition = new Position(board);

if ((value = positionValue()) != UNCLEAR) return new Best(value); if (depth == 0) transpositions.clear(); else if (depth >= 3 && depth <= 5) { Integer lookupVal = (Integer) transpositions.get(thisPosition); if (lookupVal != null) return new Best(lookupVal.intValue()); } ... chooseMove(opp, alpha, beta, depth + 1); ... if (depth <= 5) transpositions.put(thisPosition, new Integer(value)); return new Best(value, bestRow, bestColumn);}

Page 24: Anvendelser I

24

Med alpha-beta reduceres antallet af undersøgte stillinger i udgangsstillingen fra cirka 500,000 til cirka 18,000.

Effekten af beskæringerne på kryds-og-bolle

Med en transpositionstabel reduceres antallet yderligere til cirka 9,000.

Page 25: Anvendelser I

25

En generel pakke til tomandsspil med perfekt information

package twoPersonGame;

public abstract class Position { public boolean maxToMove;

public abstract ArrayList successors();

public abstract int value();

public boolean unclear() { return false; }

public int alpha_beta(int alpha, int beta, int maxLevel);

public Position bestSuccessor() { return bestSuccessor; }

private Position bestSuccessor; }

Page 26: Anvendelser I

26

public int alpha_beta(int alpha, int beta, int maxLevel) { ArrayList successors; if ((maxLevel <= 0 && !unclear()) ||

(successors = successors()).isEmpty()) return value(); for (int i = 0; alpha < beta && i < successors.size(); i++) { Position successor = (Position) successors.get(i); int value = successor.alpha_beta(alpha, beta, maxLevel - 1); if (maxToMove && value > alpha) { alpha = value; bestSuccessor = successor; } else if (!maxToMove && value < beta) { beta = value; bestSuccessor = successor; } } return maxToMove ? alpha : beta; }

Page 27: Anvendelser I

27

public int alpha_beta(int alpha, int beta, int maxLevel) { ArrayList successors; if ((maxLevel <= 0 && !unclear()) || (successors = successors()).isEmpty()) return (maxToMove ? 1 : -1) * value(); for (int i = 0; alpha < beta && i < successors.size(); i++) { Position successor = (Position) successors.get(i); int value = -successor.alpha_beta(-beta, -alpha, maxLevel-1); if (value > alpha) { alpha = value; bestSuccessor = successor; } } return alpha; }

Reduktion af kode (negamax)

Page 28: Anvendelser I

28

import twoPersonGame.*;

public class TicTacToePosition extends Position { public TicTacToePosition(int row, int column, TicTacToePosition predecessor) { ... } public ArrayList successors() { ArrayList successors = new ArrayList(); if (!isTerminal()) for (int row = 0; row < 3; row++) for (int column = 0; column < 3; column++) if (board[row][column] == '.') successors.add( new TicTacToePosition(row, column, this)); return successors; } public int value() { return isAWin('O') ? 1 : isAWin('X') ? -1 : 0; } public boolean unclear() { return value() == 0; } public boolean boardIsFull() { ... } public boolean isAWin(char symbol) { ... } public boolean isTerminal() { ... } public void print() { ... }

int row, column; char[][] board = new char[3][3];}

Page 29: Anvendelser I

29

Stakke og oversættere

Page 30: Anvendelser I

30

Check af parentesstrukturer

Problem: Givet en streng indeholdende parenteser. Afgør om parenteserne ”balancerer”, d.v.s. om der til hver venstreparentes svarer en højreparentes, og der til hver højreparentes svarer en venstreparentes.

For eksempel balancerer parenteserne i “[()]”, men ikke i “[(])”.

I det følgende forsimples problemet ved at antage, at strengen udelukkende indeholder parenteser.

Page 31: Anvendelser I

31

Kun én type parenteser

Hvis der kun er én type parenteser, f.eks. ’(’ og ’)’, er løsningen simpel.

Vi kan kontrollere balancen med en tæller.

boolean balanced(String s) { int balance = 0; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '(')

balanced++; else if (c == ')') { balance--; if (balance < 0) return false;

} } return balance == 0;}

Page 32: Anvendelser I

32

Flere typer af parenteser

Hvis der derimod er flere typer af parenteser, kan problemet ikke løses ved tællere.

Men vi kan let kontrollere balancen med en stak.

Algoritme:

1. Lav en tom stak. 2. Sålænge strengen ikke er læst, så læs det næste tegn.

a. Hvis tegnet er en startparentes, så læg det på stakken.

b. Hvis tegnet er en slutparentes, og stakken er tom, så giv en fejlmeddelelse.

c. Ellers, afstak det øverste tegn. Hvis dette ikke er en startparentes svarende til den læste slutparentes, så giv en fejlmeddelelse.

3. Hvis stakken ikke er tom, så giv en fejlmeddelelse.

Page 33: Anvendelser I

33

Eksempelparenteser: (, ), [, ], { og }

Streng s = "([]}"

(

(

[

[( (

] } Fejl!

Page 34: Anvendelser I

34

class CharStack { void push(char ch) { stack[++top] = ch; } char pop() { return stack[top--]; } boolean isEmpty() { return top == -1; }

char[] stack = new char[100]; int top = -1;

}

Page 35: Anvendelser I

35

Javakode

boolean balanced(String s) { CharStack stack = new CharStack(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '(' || c == '[' || c == '{') stack.push(c); else if (stack.isEmpty() || (c == ')' && stack.pop() != '(')) || (c == ']' && stack.pop() != '[')) || (c == '}' && stack.pop() != '{')) return false; } } return stack.isEmpty();}

Page 36: Anvendelser I

36

Indlæsning og beregning af aritmetiske udtryk

Beregn udtrykket

1 * 2 + 3 * 4

Simpel beregning fra venstre mod højre kan ikke benyttes.

Vi må tage højde for, at multiplikation har hørere præcedens end addition (* binder stærkere end +).

Det er nødvendigt at gemme mellemresultater.

Værdi = (1 * 2) + (3 * 4) = 2 + 12 = 14.

Page 37: Anvendelser I

37

Associativitet

Hvis to operatorer har samme præcedens, afgør deres associativitet beregningsrækkefølgen.

Udtrykket 4 - 3 - 2 beregnes som (4 - 3) - 2, fordi minus associerer fra venstre mod højre.

Udtrykket 4 ^ 3 ^ 2, hvor ^ betegner potensopløftning, beregnes som 4 ^ (3 ^ 2), fordi ^ associerer fra højre mod venstre.

Page 38: Anvendelser I

38

Parenteser

Beregningsrækkefølgen kan klarlægges ved hjælp af parenteser.

Eksempel:

1 - 2 - 4 * 5 ^ 3 * 6 / 7 ^ 2 ^ 2

beregnes som

( 1 - 2 ) - ( ( ( 4 * ( 5 ^ 3 ) ) * 6 ) / ( 7 ^ ( 2 ^ 2 ) ) )

Parenteserne hjælper, men det er uklart, hvorledes beregningerne kan automatiseres.

Page 39: Anvendelser I

39

Den normale notation for aritmetiske udtryk kaldes for infix-notation (operatorerne står imellem sine operander, f.eks. 3 + 4).

Postfix-notation

Beregningerne kan forenkles ved omskrivning til postfix-notation (operatorerne står efter sine operander, f.eks. 3 4 +).

1 - 2 - 4 ^ 5 * 3 * 6 / 7 ^ 2 ^ 2 (infix)

omskrives til

1 2 - 4 5 ^ 3 * 6 * 7 2 2 ^ ^ / - (postfix)

Postfix-notation er parentesfri.

Page 40: Anvendelser I

40

Beregning af postfix-udtryk ved hjælp af en stak

1 2 - 4 5 ^ 3 * 6 * 7 2 2 ^ ^ / - (postfix)

1

1

2

12

-

-1

4

-14

5

54-1

3

-11024

3

*

-13072

^

-11024

6

-13072

6

*

-118432

2

-1

718432

2

2

18432

7

-1

7

2

-1

718432

2

^

-1

718432

4

^

-1

240118432

/

-17

-8

-

Page 41: Anvendelser I

41

class Calculator { static int valueOf(String str) { IntStack s = new IntStack(); for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (Character.isDigit(c)) s.push(Character.getNumericValue(c)); // only one digit else if (!Character.isWhitespace(c)) { int rhs = s.pop(), lhs = s.pop(); switch(c) { case '+': s.push(lhs + rhs); break; case '-': s.push(lhs - rhs); break; case '*': s.push(lhs * rhs); break; case '/': s.push(lhs / rhs); break; case '^': s.push((int) Math.pow(lhs, rhs)); break; } } } return s.pop(); }}

Page 42: Anvendelser I

42

Omformning fra infix til postfix ved hjælp af en stak

(Dijkstras vigesporsalgoritme)

Infix-streng Postfix-strengoperander

operatorer

Operatorstak

Page 43: Anvendelser I

43

Omformning fra infix til postfix

1 - 2 - 4 ^ 5 * 3 * 6 / 7 ^ 2 ^ 2 (infix)

1

1

2

-

12

-

-

12-

-

1

-

4

-

12-4

^

-^

12-4

5

-^

12-45

*

-*

12-45^

3

12-45^3

-*

*

12-45^3*

-*

6

12-45^3*6

-*

/

12-45^3*6*

-/

7

12-45^3*6*7

-/

^

12-45^3*6*7

-/

^

2

12-45^3*6*72

-/

^

^

-/

^

^

2

12-45^3*6*722^^/-

Page 44: Anvendelser I

44

Syntaksanalyse

Mål: Et program til indlæsning og beregning af aritmetiske udtryk.

Løs et lettere problem først: Læs en streng og undersøg, om den udgør et lovligt aritmetisk udtryk.

Page 45: Anvendelser I

45

Benyt en grammatik til at beskrive aritmetiske udtryk:

<expression> ::= <term> | <term> + <expression> |

<term> - <expression><term> ::= <factor> | <factor> * <term> | <factor> / <term><factor> ::= <number> | (<expression>)

Grammatik for aritmetiske udtryk

Grammatikken er beskrevet ved produktionsregler og består af(1) nonterminale symboler: expression, term, factor og

number. (2) terminale symboler: +, -, *, /, (, ) og cifre.

(3) metasymboler: ::=, <, >, og | .

Page 46: Anvendelser I

46

En streng er et aritmetisk udtryk, hvis det ved hjælp af produktionsreglerne er muligt at udlede strengen ud fra expression, dvs. ud fra expression i en række skridt nå frem til strengen ved i hvert skridt at erstatte et nonterminal-symbol med et af alternativerne på højresiden af en produktion for dette symbol.

Syntaksanalyse

Syntakstræ for (3*5+4/2)-1

expression term -

expression

factor

term

( expression )

factor

term + expression

number

factor * factor term 1

number number factor / factor

3 5 number number

4 2

Page 47: Anvendelser I

47

Syntaksdiagrammer

expression:term

+

-

term:factor

*

/

factor:

number

expression( )

Page 48: Anvendelser I

48

Et rekursivt Java-program til syntaksanalyse kan konstrueres direkte ud fra syntaksdiagrammerne.

Syntaksanalyse ved rekursiv nedstigning (top-down parsing)

void expression() { term(); while (token == PLUS || token == MINUS) { getToken(); term(); }}

static final int PLUS = 1, MINUS = 2, MULT = 3, DIV = 4, LPAR = 5, RPAR = 6, NUMBER = 7, EOS = 8; int token;

Page 49: Anvendelser I

49

void factor() { if (token == NUMBER) ; else if (token == LPAR) { getToken(); expression(); if (token != RPAR) error("missing right paranthesis"); } else error("illegal factor: " + token); getToken();}

void term() { factor(); while (token == MULT || token == DIV)

{ getToken(); factor(); }}

Page 50: Anvendelser I

50

StringTokenizer str;

void parse(String s) { str = new StringTokenizer(s,"+-*/() ",true); getToken(); expression();}

Eksempel på kald:

parse("(3*5+4/2)-1");

Page 51: Anvendelser I

51

void getToken() { String s; try { s = str.nextToken(); } catch(NoSuchElementException e) { token = EOS; return; } if (s.equals(" ")) getToken(); else if (s.equals("+")) token = PLUS; else if (s.equals("-")) token = MINUS; else if (s.equals("*")) token = MULT; else if (s.equals("/")) token = DIV; else if (s.equals("(")) token = LPAR; else if (s.equals(")")) token = RPAR; else { try { Double.parseDouble(s); token = NUMBER; } catch(NumberFormatException e) { error("number expected"); } }}

Page 52: Anvendelser I

52

Beregning af aritmetiske udtryk

Beregning kan opnås ved få simple ændringer af syntaksanalyse-programmet.

Analysemetoderne skal returnere med deres tilhørende værdi (i stedet for void).

double valueOf(String s) { str = new StringTokenizer(s,"+-*/() ",true); getToken(); return expression();}

Eksempel på kald:

double r = valueOf("(3*5+4/2)-1");

Page 53: Anvendelser I

53

double term() { double v = factor(); while (token == MULT || token == DIV) if (token == MULT) { getToken(); v *= factor(); } else { getToken(); v /= factor(); } return v;}

double expression() { double v = term(); while (token == PLUS || token == MINUS) if (token == PLUS) { getToken(); v += term(); } else { getToken(); v -= term(); } return v;}

Page 54: Anvendelser I

54

double factor() { double v; if (token == NUMBER) v = value; else if (token == LPAR) { getToken(); v = expression(); if (token != RPAR) error("missing right paranthesis"); } else error("illegal factor: " + token); getToken(); return v;}

Page 55: Anvendelser I

55

void getToken() { String s; try { s = str.nextToken(); } catch(NoSuchElementException e) { token = EOS; return; } if (s.equals(" ")) getToken(); else if (s.equals("+")) token = PLUS; else if (s.equals("-")) token = MINUS; else if (s.equals("*")) token = MULT; else if (s.equals("/")) token = DIV; else if (s.equals("(")) token = LPAR; else if (s.equals(")")) token = RPAR; else { try{ value = Double.parseDouble(s); token = NUMBER; } catch(NumberFormatException e) { error("number expected"); } }}

Page 56: Anvendelser I

56

• Læs kapitel 12 og 13

• Løs følgende opgaver

Opgave 23: 10.2 (1 point) Opgave 24: 11.2 (1 point) Opgave 25: 11.3 (2 point)Opgave 26: Se de næste sider (3 point, ikke-obligatorisk)

Afleveringsfrist: tirsdag den 13. november

Ugeseddel 730. oktober - 6. november

Page 57: Anvendelser I

57

fortsættes

På de følgende sider er angivet en Java-klasse til udregning af aritmetiske udtryk. Klassener tilgængelig fra kursets hjemmeside via henvisningen “Kode til opgaver”.

Tilføj muligheden for at udregne aritmetiske udtryk, som indeholder potensopløftning.Som symbol for potensopløftning anvendes ^. Bemærk at ^ er højre-associativ. Såledesudregnes 2^2^3 som 2^(2^3).

import java.util.*;

public class Calculator { static final int PLUS = 0, MINUS = 1, MULT = 2, DIV = 3, LPAR = 4, RPAR = 5, CONST = 6, EOS = 7; int token; double value; StringTokenizer str;

double valueOf(String s) { str = new StringTokenizer(s, "+-*/()= ", true); getToken(); return expression(); }

double expression() { double v = term(); while (token == PLUS || token == MINUS) if (token == PLUS) { getToken(); v += term(); } else { getToken(); v -= term(); } return v; }

Page 58: Anvendelser I

58

fortsættes

double term() { double v = factor(); while (token == MULT || token == DIV) if (token == MULT) { getToken(); v *= factor(); } else { getToken(); v /= factor(); } return t; }

double factor() { double v = 0; if (token == CONST) v = value; else if (token == LPAR) { getToken(); value = expression(); if (token != RPAR) error("missing right paranthesis"); } else error("illegal factor"); getToken(); return v; }

void error(String msg) { throw new RuntimeException(msg); }

Page 59: Anvendelser I

59

void getToken() { String s; try { s = str.nextToken(); } catch(NoSuchElementException e) { token = EOS; return; } if (s.equals(" ")) getToken(); else if (s.equals("+")) token = PLUS; else if (s.equals("-")) token = MINUS; else if (s.equals("*")) token = MULT; else if (s.equals("/")) token = DIV; else if (s.equals("(")) token = LPAR; else if (s.equals(")")) token = RPAR; else { try { value = Double.valueOf(s).doubleValue(); token = CONST; } catch(NumberFormatException e) { error("constant expected"); } } }

public static void main(String arg[]) { Calculator calc = new Calculator(); System.out.println(calc.valueOf("3*2+4*5")); }}