1.01k likes | 1.18k Views
In 1924 the renowned Polish logician Jan Łukasiewicz (1878 – 1956) was trying to simplify the rules for interpreting logical formulae.
E N D
In 1924 the renowned Polish logician Jan Łukasiewicz (1878 – 1956) was trying to simplify the rules for interpreting logical formulae. • The standard rules are rather complex: for example, let us suppose that p, q, r, s and t are propositions (i.e., variables that have boolean values). Then to evaluate • (p or q) and r and not (s and (t or u)) • we must take into account: • the priority of the various operators (not stronger than and, and stronger than or); • parentheses (find the matching right parenthesis, nested expressions must be evaluated first etc.) • If you try to formulate the precise rules that would allow you to evaluate such expressions quite mechanically, you find that the situation is rather messy. Stacks
In 1924 the renowned Polish logician Jan Łukasiewicz (1878 – 1956) was trying to simplify the rules for interpreting logical formulae. • The standard rules are rather complex: for example, let us suppose that p, q, r, s and t are propositions (i.e., variables that have boolean values). Then to evaluate • (p or q) and r and not (s and (t or u)) • we must take into account: • the priority of the various operators (not stronger than and, and stronger than or); • parentheses (find the matching right parenthesis, nested expressions must be evaluated first etc.) • If you try to formulate the precise rules that would allow you to evaluate such expressions quite mechanically, you find that the situation is rather messy. • Łukasiewicz came up with a parenthesis-free notation that is less readable for people, but is quite unambiguous. The above expression would be written as • and and or p q r not and s or t u • This became known as “the Polish notation”. Stacks
(p or q) and r and not (s and (t or u)) and and or p q r not and s or t u The advantages of the Polish notation are clear. To evaluate an expression you must only be aware of the arity of each operator, i.e., the number of arguments that it takes. Then you process the expression from left to right, as follows: Stacks
(p or q) and r and not (s and (t or u)) and and or p q r not and s or t u The advantages of the Polish notation are clear. To evaluate an expression you must only be aware of the arity of each operator, i.e., the number of arguments that it takes. Then you process the expression from left to right, as follows: andand or p q r not and s or t u and e1 e2 Stacks
(p or q) and r and not (s and (t or u)) and and or p q r not and s or t u The advantages of the Polish notation are clear. To evaluate an expression you must only be aware of the arity of each operator, i.e., the number of arguments that it takes. Then you process the expression from left to right, as follows: and and or p q r not and s or t u and e1 e2 where e1 = and e3 e4 Stacks
(p or q) and r and not (s and (t or u)) and and or p q r not and s or t u The advantages of the Polish notation are clear. To evaluate an expression you must only be aware of the arity of each operator, i.e., the number of arguments that it takes. Then you process the expression from left to right, as follows: and andor p qr not and s or t u and e1 e2 where e1 = and e3 e4 where e3 = or p q Stacks
(p or q) and r and not (s and (t or u)) and and or p q r not and s or t u The advantages of the Polish notation are clear. To evaluate an expression you must only be aware of the arity of each operator, i.e., the number of arguments that it takes. Then you process the expression from left to right, as follows: and and or p q r not and s or t u and e1 e2 where e1 = and e3 e4 where e3 = or p q e4 = r Stacks
(p or q) and r and not (s and (t or u)) and and or p q r not and s or t u The advantages of the Polish notation are clear. To evaluate an expression you must only be aware of the arity of each operator, i.e., the number of arguments that it takes. Then you process the expression from left to right, as follows: and and or p q r not and s or t u and e1 e2 where e1 = and e3 e4 where e3 = or p q e4 = r e2 = not e5 Stacks
(p or q) and r and not (s and (t or u)) and and or p q r not and s or t u The advantages of the Polish notation are clear. To evaluate an expression you must only be aware of the arity of each operator, i.e., the number of arguments that it takes. Then you process the expression from left to right, as follows: and and or p q r not and s or t u and e1 e2 where e1 = and e3 e4 where e3 = or p q e4 = r e2 = not e5 where e5 = and s e6 Stacks
(p or q) and r and not (s and (t or u)) and and or p q r not and s or t u The advantages of the Polish notation are clear. To evaluate an expression you must only be aware of the arity of each operator, i.e., the number of arguments that it takes. Then you process the expression from left to right, as follows: and and or p q r not and s or t u and e1 e2 where e1 = and e3 e4 where e3 = or p q e4 = r e2 = not e5 where e5 = and s e6 where e6 = or t u Stacks
(p or q) and r and not (s and (t or u)) and and or p q r not and s or t u The advantages of the Polish notation are clear. To evaluate an expression you must only be aware of the arity of each operator, i.e., the number of arguments that it takes. Then you process the expression from left to right, as follows: and and or p q r not and s or t u and e1 e2 where e1 = and e3 e4 where e3 = or p q e4 = r e2 = not e5 where e5 = and s e6 where e6 = or t u And now, if we know the values of p, q, r, s, t and u, the evaluation of the expression becomes trivial. Stacks
(p or q) and r and not (s and (t or u)) and and or p q r not and s or t u Today we would say that Łukasiewicz’s notation is simply the result of a pre-order traversal of the Abstract Syntax Tree (AST) of the expression: and and not or r and p q s or t u Stacks
When people started to think about evaluating expressions in programming languages, they quickly realised that Łukasiewicz had a point, but that it would actually be convenient to change from prefix notation to postfix notation, so that the operands are processed before the operators. This modified version became known as the Reverse Polish Notation (RPN). Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * The tree is provided only for clarity. There is a simple algorithm for converting a “normal” expression to its RPN form without using a tree. Remember, memory was at a premium, trees require lots of space (two addresses + info per node) and are tricky to process without recursion. This is the unary minus, which must be distinguished from the binary subtraction operator. Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. 2 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 21 + 5 2 21 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. 2 1 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 21 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. 2 1 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. 3 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + +4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. 3 4 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + +4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. 3 4 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. -1 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. -1 5 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 52 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. -1 5 2 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 52 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. -1 5 2 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. -1 7 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. -1 7 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. -1 -7 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 –6 2 1 + 5 2 2 1 + 4 – 5 2 + –16 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. -1 -7 6 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 –6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. -1 -7 6 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * –+ + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. -1 -1 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * –+ + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. -1 -1 Stacks
Reverse Polish Notation (RPN) is a trace of a post-order traversal of an AST. (2 + 1 – 4) * ( – (5 + 2) + 6) * – + + 4 – 6 2 1 + 5 2 2 1 + 4 – 5 2 + –1 6 + * Evaluation of an expression expressed in RPN is quite trivial: all we need is a stack. 1 Stacks
2 1 + 4 – 5 2 + –1 6 + * As we saw, RPN simply expresses the natural order of execution: when it is an operator’s turn to be “executed”, its operands have already been evaluated and their values are on top of the stack. Stacks
2 1 + 4 – 5 2 + –1 6 + * As we saw, RPN simply expresses the natural order of execution: when it is an operator’s turn to be “executed”, its operands have already been evaluated and their values are on top of the stack. This principle can also be applied to user-defined functions. When we have a call such as foo( exp1, exp2, exp3 ) we can execute it as follows: Stacks
2 1 + 4 – 5 2 + –1 6 + * As we saw, RPN simply expresses the natural order of execution: when it is an operator’s turn to be “executed”, its operands have already been evaluated and their values are on top of the stack. This principle can also be applied to user-defined functions. When we have a call such as foo( exp1, exp2, exp3 ) we can execute it as follows: 1. Evaluate exp1, leaving its value on the stack. Stacks
2 1 + 4 – 5 2 + –1 6 + * As we saw, RPN simply expresses the natural order of execution: when it is an operator’s turn to be “executed”, its operands have already been evaluated and their values are on top of the stack. This principle can also be applied to user-defined functions. When we have a call such as foo( exp1, exp2, exp3 ) we can execute it as follows: 1. Evaluate exp1, leaving its value on the stack. 2. Evaluate exp2, leaving its value on the stack. Stacks
2 1 + 4 – 5 2 + –1 6 + * As we saw, RPN simply expresses the natural order of execution: when it is an operator’s turn to be “executed”, its operands have already been evaluated and their values are on top of the stack. This principle can also be applied to user-defined functions. When we have a call such as foo( exp1, exp2, exp3 ) we can execute it as follows: 1. Evaluate exp1, leaving its value on the stack. 2. Evaluate exp2, leaving its value on the stack. 3. Evaluate exp3, leaving its value on the stack. Stacks
2 1 + 4 – 5 2 + –1 6 + * As we saw, RPN simply expresses the natural order of execution: when it is an operator’s turn to be “executed”, its operands have already been evaluated and their values are on top of the stack. This principle can also be applied to user-defined functions. When we have a call such as foo( exp1, exp2, exp3 ) we can execute it as follows: 1. Evaluate exp1, leaving its value on the stack. 2. Evaluate exp2, leaving its value on the stack. 3. Evaluate exp3, leaving its value on the stack. 4. Invoke foo(): it should pick the arguments from the stack and replace them with the returned value. Stacks
2 1 + 4 – 5 2 + –1 6 + * As we saw, RPN simply expresses the natural order of execution: when it is an operator’s turn to be “executed”, its operands have already been evaluated and their values are on top of the stack. This principle can also be applied to user-defined functions. When we have a call such as foo( exp1, exp2, exp3 ) we can execute it as follows: 1. Evaluate exp1, leaving its value on the stack. 2. Evaluate exp2, leaving its value on the stack. 3. Evaluate exp3, leaving its value on the stack. 4. Invoke foo(): it should pick the arguments from the stack and replace them with the returned value. 5. The value of the function is on top of the stack. Stacks
2 1 + 4 – 5 2 + –1 6 + * As we saw, RPN simply expresses the natural order of execution: when it is an operator’s turn to be “executed”, its operands have already been evaluated and their values are on top of the stack. This principle can also be applied to user-defined functions. When we have a call such as foo( exp1, exp2, exp3 ) we can execute it as follows: 1. Evaluate exp1, leaving its value on the stack. 2. Evaluate exp2, leaving its value on the stack. 3. Evaluate exp3, leaving its value on the stack. 4. Invoke foo(): it should pick the arguments from the stack and replace them with the returned value. 5. The value of the function is on top of the stack. This meshes quite well with the usual way of handling activation of potentially recursive functions. Let us look at the details. Stacks
Most modern programming languages have very similar runtime models. The memory occupied by a program can be organized like this: Code The stack Free space The heap Stacks
Most modern programming languages have very similar runtime models. The memory occupied by a program can be organized like this: The program, translated to machine code Code The stack Free space The heap Stacks
Most modern programming languages have very similar runtime models. The memory occupied by a program can be organized like this: The program, translated to machine code Code The recursion stack (a.k.a. the execution stack) The stack Free space The heap Stacks
Most modern programming languages have very similar runtime models. The memory occupied by a program can be organized like this: The program, translated to machine code Code The recursion stack (a.k.a. the execution stack) The stack Free space Storage for long-lived objects The heap Stacks
Most modern programming languages have very similar runtime models. The memory occupied by a program can beorganized like this: The program, translated to machine code Code The recursion stack (a.k.a. the execution stack) The stack Free storage: room for the stack or the heap to grow Free space Storage for long-lived objects The heap Stacks
A stack is simulated by storing items in memory. A special variable (or a register) indicates the top of the stack. We push by adding stuff to free memory and modifying the index of the top, and pop simply by modifying the index of the top. item 1 item 2 item 3 item 4 item 5 We normally show the stack growing downwards, because we tend to show low addresses at the top of a drawing. u n u s e d Stacks
A stack is simulated by storing items in memory. A special variable (or a register) indicates the top of the stack. We push by adding stuff to free memory and modifying the index of the top, and pop simply by modifying the index of the top. item 1 item 2 item 3 item 4 item 5 We normally show the stack growing downwards, because we tend to show low addresses at the top of a drawing. u n u s e d Stacks
When a procedure or function is invoked, an activation record (a.k.a. an environment frame) is pushed onto the recursion stack. A special variable (or register) CF shows the address of the topmost frame. NOTE: To keep things simple, we discuss the run-time model of “flat” languages, such as C: they are “flat”, because procedure declarations do not occur inside other procedure declarations. Languages that do not have this property (Algol-60, Pascal…) require a more complicated model. v space for arguments address of previous frame CF the trace (where to return) space for local variables stack of temporary values Stacks
When a procedure or function is invoked, an activation record (a.k.a. an environment frame) is pushed onto the recursion stack. A special variable (or register) CF shows the address of the topmost frame. CFstands for Current Frame . v space for arguments address of previous frame CF the trace (where to return) space for local variables stack of temporary values Stacks