(8 - 2) * 3 + 4 needs brackets and rules to be read. Watch a stack rewrite it in an order that needs neither.
Rewriting a sum for a machine
Digits pass straight through; operators wait on a stack
void to_postfix(const char *in, char *out){ char ops[MAX]; int top = 0; size_t k = 0; for (size_t i = 0; in[i] != '\0'; i++) { char c = in[i]; if (c >= '0' && c <= '9') { emit(out, &k, c); } else if (c == '(') { ops[top] = c; top++; } else if (c == ')') { while (ops[top - 1] != '(') { top--; emit(out, &k, ops[top]); } top--; } else if (c == '+' || c == '-' || c == '*' || c == '/') { while (top > 0 && ops[top - 1] != '(' && prec(ops[top - 1]) >= prec(c)) { top--; emit(out, &k, ops[top]); } ops[top] = c; top++; } } while (top > 0) { top--; emit(out, &k, ops[top]); } out[k] = '\0';}int main(void){ const char *exprs[3] = { "(8 - 2) * 3 + 4", "8 - 2 * 3 + 4", "8 - 2 - 1" }; char out[MAX]; for (int i = 0; i < 3; i++) { to_postfix(exprs[i], out); printf("%-16s-> %s\n", exprs[i], out); } return 0;}i is 0: rewrite "(8 - 2) * 3 + 4".
Output
Describe this picture
Lines 20 to 52 (to_postfix) and 54 to 63 (main) of postfix.c, with the line about to run marked; while emit or prec runs, the line that called it is marked. Beside the code is a picture in three rows. At the top, labelled IN, is the expression as a row of character boxes, with narrow gaps for the spaces. The character being read has a ring around it; those already read are dimmed. In the middle, labelled OPS, is to_postfix’s char array ops standing upright, ops[0] at the bottom, cells 0 to 3 drawn and a dashed stub for the other sixty. top is a bold line across it at the boundary above the last waiting operator, labelled “top” and its value. At the bottom, labelled OUT, are the postfix tokens written to main’s out so far, operators tinted; a new token rises into place. The clip plays once, about 18 seconds, one beat per token. ( lands on the stack. 8 drops into OUT. - lands on top of (, and 2 drops into OUT. At ), the - goes out to OUT and then the ( is dropped, leaving the stack empty. * waits; 3 goes out. At +, the words “prec('') 2 ≥ prec(’+’) 1” and ”'' leaves first” appear beside the stack; * goes out, then + waits. 4 goes out. At the end of the expression the + goes out, and OUT reads 8 2 - 3 * 4 +. Under the picture are one sentence on the current step and the output, which ends (8 - 2) * 3 + 4 -> 8 2 - 3 * 4 +. The transport plays and pauses, steps one beat back or forward, and scrubs; a paused step is kept in the page address as rewrite.step.
Three ways to write a sum
The usual way to write a sum puts each operator between its two operands: 8 - 2. That is infix. It needs rules to be read: * before +, and brackets when you want something else. 8 - 2 * 3 is 2, but (8 - 2) * 3 is 18.
Postfix puts the operator after its operands: 8 2 -. Prefix puts it before: - 8 2. In both, the order of the tokens says exactly what to do, so neither needs brackets or precedence rules:
| Infix | Postfix | Prefix |
|---|---|---|
8 - 2 | 8 2 - | - 8 2 |
(8 - 2) * 3 + 4 | 8 2 - 3 * 4 + | + * - 8 2 3 4 |
8 - 2 * 3 + 4 | 8 2 3 * - 4 + | + - 8 * 2 3 4 |
Postfix is not the expression reversed. The digits stay in the order they were written; only the operators move, each to just after the operands it works on.
The code and its cost
The rules
Read the infix expression from left to right, with a stack for operators:
- A digit goes straight to the output.
- An operator first sends out every waiting operator of higher or equal precedence (stopping at a
(), then waits on the stack itself. - A
(waits on the stack. - A
)sends out every operator above its(, then the(is dropped. - At the end, every operator still waiting goes out.
* and / have precedence 2, + and - precedence 1. An operator that must be done first leaves the stack first, so it lands in the output first.
The function
void to_postfix(const char *in, char *out)
{
char ops[MAX];
int top = 0;
size_t k = 0;
for (size_t i = 0; in[i] != '\0'; i++) {
char c = in[i];
if (c >= '0' && c <= '9') {
emit(out, &k, c);
} else if (c == '(') {
ops[top] = c;
top++;
} else if (c == ')') {
while (ops[top - 1] != '(') {
top--;
emit(out, &k, ops[top]);
}
top--;
} else if (c == '+' || c == '-' || c == '*' || c == '/') {
while (top > 0 && ops[top - 1] != '(' && prec(ops[top - 1]) >= prec(c)) {
top--;
emit(out, &k, ops[top]);
}
ops[top] = c;
top++;
}
}
while (top > 0) {
top--;
emit(out, &k, ops[top]);
}
out[k] = '\0';
}
ops and top are a stack of chars, as in Matching brackets. emit(out, &k, c) writes c into out at position k, with a space before it unless it is the first token, and moves k on; it takes k’s address so that it can change to_postfix’s k. prec returns 2 for * and / and 1 for the others. The input must be well formed, with single digits; spaces are skipped.
Equal precedence and left associativity
The test is >=, not >. In 8 - 2 - 1, when the second - arrives, the first is waiting with the same precedence, so it leaves first: the output is 8 2 - 1 -, which means (8 − 2) − 1 = 5. That is what 8 - 2 - 1 means in arithmetic: operators of equal precedence are done from the left, which is called left associativity.
What it costs
Each character is read once. Each operator is pushed once and popped once. So an expression of n characters takes about n steps (O(n) in the notation of lesson 3.3), and the stack never holds more than the operators and brackets.
Running it yourself
gcc -std=c11 -Wall -Wextra -pedantic postfix.c -o postfix
./postfix
(8 - 2) * 3 + 4 -> 8 2 - 3 * 4 +
8 - 2 * 3 + 4 -> 8 2 3 * - 4 +
8 - 2 - 1 -> 8 2 - 1 -
In the second, * waits on top of - because its precedence is higher (2 > 1); then + sends out * and then -. At that point two operators were waiting at once.
Worked example
(1 + 2) * (3 + 4). ( waits; 1 out; + waits above (; 2 out; ) sends + out and drops (. * waits. ( waits above *; 3 out; + waits above (, which stops it from looking at *; 4 out; ) sends + out. The end sends * out: 1 2 + 3 4 + *.
With > instead of >=. For 8 - 2 - 1, the second - would not send the first out, so both wait and leave at the end: 8 2 1 - -. That means 8 − (2 − 1) = 7, not 5. One character in the condition decides which way equal operators group.
Prefix. The prefix form of (8 - 2) * 3 + 4 is + * - 8 2 3 4: the last operation done, +, comes first.
Questions. The postfix of 2 * 3 + 4 * 5 is 2 3 * 4 5 * +. For 8 - 2 * 3 + 4, at most two operators wait at once (- and *).
Where it’s used
Compilers and calculators. A compiler reading a + b * c must decide what to do first; turning the expression into postfix, or into the tree that postfix describes, is how it records that decision. The method above is the classic way to do it.
Spreadsheets. A formula such as =(B2 - B3) * 2 is parsed the same way before it is worked out.
What comes next. Evaluating postfix (8.4) works out 8 2 - 3 * 4 + with a second stack, this time of numbers. Reading an expression tree in postorder gives its postfix (12.3).
Reference card
| Code or idea | Meaning |
|---|---|
| infix, postfix, prefix | a + b, a b +, + a b |
| digit | straight to the output |
| operator | send out waiting operators of higher or equal precedence, then wait |
( / ) | wait / send out everything above its (, then drop it |
| end | send out everything still waiting |
>= in the test | equal operators group from the left |
| cost | one pass, about n steps (O(n), lesson 3.3) |
Platform: C11, gcc on x86-64 Linux: char 1 byte, int 4, size_t 8, little-endian.