Outline
- expressions, parsing, evaluating
- infix expressions
- Java types and operators
- file I/O
- linked list implementation of stack ADT
- nodes
- linked lists
infix expressions
- 2 + 3 * 4 has the value?
- some operators (*, /, %) have higher precedence than other
operators (+, -)
- operators with the same precedence are evaluated in left-to-right order
- these expression can be evaluated using two stacks:
- when an operand is read, it is pushed onto the operand stack
- operators are:
- pushed onto the operator stack if the new operator has higher
precedence than the old operator
- otherwise, the top of the operator stack is popped and evaluated with
the top two elements of the operand stack, the result is pushed onto the
operand stack, and the new operator is pushed onto the operator stack
- at the end of the expression, operators are popped off and evaluated
(popping the operands and pushing the results) until the operator stack is empty
- at this point, the operand stack should have exactly one number in it
- more interesting with more precedence levels, e.g. ^ (exponentiation)
- for this assignment, all operands in the expression are single-digit
(but results in the operand stack may be greater than 9), to make parsing
easier
- the Java compiler must recognize (parse) Java expressions
and either
evaluate any constant-valued (sub-) expressions, or
emit code to evaluate the expression at run-time
integer operators
- addition (+), subtraction (-), multiplication (*) work as expected
- division (/) always rounds down: 3 / 2 gives 1, 101 / 100 gives 1
- remainder (%), also known as modulo, returns the remainder from the
division: 3 % 2 gives 1, 127 % 100 gives 27
- multiplication, division, and modulo have higher precedence than
addition and subtraction, and so are evaluated first: 3 + 54 * 17
is 3 + (54 * 17)
- with equal-precedence operators, the expression is evaluated from
left to right:
99 - 3 - 33 / 11 / 3 is ((99 - 3) - ((33 / 11) / 3))
other kinds of expressions
- in a prefix expression, the operands come before the operands:
/ + 3 * 7 4 2 means 3 + (7 * 4) / 2
- in a postfix expression, the operands come after the operands:
4 2 / 3 + 1 * means (4 / 2 + 3) * 1
- in an infix expression, the operands come in-between the operands
- only infix expressions need:
- precedence
- parentheses (to override precedence)
in prefix and postfix expression, the position indicates which operands
are used with which operators
- converting from one notation to the other can benefit from
using a stack or recursion
- computing in prefix or postfix is easy when using a stack
algorithm for postfix computation
- read the next input (next character) of the string
- if the character is an operand, push it on the stack
- if the character is an operator, pop the top two elements
of the stack, apply the corresponding operation
(the operands must be in the correct order!), and push the result back
on the stack
- if the string is empty:
- if the stack has one element, that element is the result
- if the stack has 0 or multiple elements, the expression is
malformed (or the code is buggy)
- in-class exercise: evaluate the following expressions:
97/
12*3*4*
34*12+-
objects and primitives
Boxing
- boxing means using a pointer (reference) to represent something
- for example, an Integer is a boxed form of an int
- Java 5 boxes and unboxes automatically:
reading from a file
Reminder: stack ADT
- void push(String value) adds the value to the top of the stack
- String pop() throws EmptyStackException removes the
value on top of the stack and returns it (or throws the exception)
- boolean empty() reports whether the stack is empty
- also String peek() to just return the top of the stack
array implementation, linked list implementation
- in the array implementation, had to use a fixed-size structure,
an array, to store a variable amount of data
- this makes things complicated, e.g. when having to create a new,
bigger array
- to really do this well, we might also want to use a smaller array
when the stack becomes small
- instead of a single structure that we try to make the right size,
we could use a variable number of fixed-size objects, which
we call nodes
- each node has a field that refers to (points to) the next node, if
any
- the last node has a null reference (pointer) to the "next" node
- the result is a linked list of nodes
StringNode class
public class StringNode {
private String item;
private StringNode next;
/* methods */
}
- the first data item references the string that this node keeps track of
- the second item references the next node, if any -- this may be
null
- some useful methods:
- public StringNode(String value, Node next), the constructor
- public String value() and
public String next(), the two
accessor methods
- possibly could have public void setNext(Node next),
the only mutator method
- see also the complete implementation
In-class exercise
On your own, draw a diagram of the result of this code:
StringNode node1 = new StringNode("string1", null);
StringNode node2 = new StringNode("string2", null);
StringNode node3 = new StringNode("string3", null);
StringNode node4 = new StringNode("string4", null);
StringNode node5 = new StringNode("string5", null);
node1.setNext(node5);
node2.setNext(node4);
node3.setNext(node2);
node4.setNext(null);
node5.setNext(node3);
Linked list stack implementation
- the stack is implemented with a single reference to a node
- if the reference is null, the stack is empty
- if the reference points to a node, that node's item is the
top element of the stack
- the remainder of the list contains the remainder of the stack
- this is a recursive definition!
/*
public class StringLinkedStack implements StringStackInterface {
/* only need to store a single pointer to the node holding
* the top of the stack.
* The pointer is null if the stack is empty.
*/
private StringNode top;
/* no-arguments default constructor creates an empty stack */
public StringLinkedStack() {
top = null; // start with an empty stack
}
- two methods are particularly interesting: push and pop
-
/* @param string to push onto the stack */
public void push(String value) {
// create a new string node to hold the new top, and a
// reference to the old stack
StringNode newTop = new StringNode(value, top);
// now have our top of stack point to this new node
top = newTop;
}
or, even simpler,
public void push(String value) {
top = new StringNode(value, top);
}
-
/* @return the top string on the stack */
public String pop() throws EmptyStackException {
if (empty()) {
throw new EmptyStackException();
}
String result = top.getValue();
top = top.getNext();
return result;
}
- see also the
complete implementation
Linked list stack performance
- in-class exercise: what is the run-time performance of push?
- in-class exercise: what is the run-time performance of pop?
Java notes
- top is a reference to a Node, and is not itself
a node
- likewise next in the Node class
- pointer tutorial
Linked lists
- linked lists can be used for much more than stacks
- each node has a field to refer to the next node, and also the
local data for the node
- the above list could be for a stack with four elements, but could
be used for other purposes as well
Object usage
- a static method is called independently of any objects
- non-static methods must be called by reference to an object,
e.g.
top.getNext()
- what happens if top is null?
- NullPointerException!
Stack usage: balanced parentheses
- balanced parenteses: "(a (b c) [d (e)] f g)"
- unbalanced parenteses: "(a (b c {d (e)) f g]"
- algorithm to check for balanced parentheses:
- when encountering an open parentheses, put it on the stack
- when encountering a closed parentheses, remove the matching
one from the top of the stack, or declare an error
- at the end of the string, should have an empty stack
In-class exercise
everyone together, figure out what
this code does