Skip to content

Instantly share code, notes, and snippets.

@njdart
Created September 13, 2018 08:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save njdart/7a741063db5ccf14cfe6a553b25a2cfb to your computer and use it in GitHub Desktop.
Save njdart/7a741063db5ccf14cfe6a553b25a2cfb to your computer and use it in GitHub Desktop.
// this program is a simple implementation of a reverse polish calculator using an array as a simple stack
// see https://en.wikipedia.org/wiki/Reverse_Polish_notation
package main
import (
"fmt"
"reflect"
)
// We define a stack as an array of integer
type stack []int
// push takes a stack and a value and appends the value onto our "stack" and return a new stack
func push(s stack, v int) stack {
return append(s, v)
}
// pop takes a stack and returns a new stack (minus the head value) and the head value
func pop(s stack) (stack, int) {
l := len(s)
return s[:l-1], s[l-1]
}
// rpnCalc takes any number of arguments of either integer or string, and tries to do a reverse-polish calculation with the value.
// int value are operands
// string values are operators. Valid operators are addition, subtraction, multiplication and division
// rpnCalc returns an integer result, and possibly an error if an operator is not valid, or the RPN calculation is malformed
func rpnCalc(args ...interface{}) (int, error) {
// make a new stack to store our values (operands) in
stk := stack{}
// for each argument in the function call
for _, arg := range args {
switch d := arg.(type) {
// if the argument in a integer, it's an operand, add it to the stack
case int:
stk = push(stk, d)
// if the argument is a string, it's an operator
case string:
// first, check if the stack size is sufficient to support an operator at this point. All operators require
// two values to operate with
if len(stk) < 2 {
return 0, fmt.Errorf("Stack had less than two operands, error in calculation. %+v", stk)
}
// pop our two operands off the stack
var operandA, operandB int
stk, operandB = pop(stk)
stk, operandA = pop(stk)
// depending on the operator (string) value, do the appropriate operation
switch d {
case "+":
stk = push(stk, operandA+operandB)
case "-":
stk = push(stk, operandA-operandB)
case "*":
stk = push(stk, operandA*operandB)
case "/":
stk = push(stk, operandA/operandB)
// if the operator isn't in the above cases, we don't support it. return an error
default:
return 0, fmt.Errorf("Invalid operator %s", d)
}
// if the argument isn't of type int or string, we don't support it
default:
return 0, fmt.Errorf("Invalid argument type %s (value %v)", reflect.TypeOf(arg), arg)
}
}
// we've finished the calculation. Check the stack has only one item left on it. If it has more then
// the calculation was malformed (eg 1, 2, 3, "+")
if len(stk) != 1 {
return 0, fmt.Errorf("Stack was not len 1 at end of calculation, error in calculation: %+v", stk)
}
// otherwise, if there is only one value left, that's our result
return stk[0], nil
}
func main() {
// ( ( (5 + 7) * 12) - 100) / 4 = 11
// 5 + 7 = 12
// ( 12 ) * 12 = 144
// ( 144 ) - 100 = 44
// 44 / 4 = 11
result, err := rpnCalc(5, 7, "+", 12, "*", 100, "-", 4, "/")
if err != nil {
fmt.Println("Error in RPN calculation:", err)
} else {
fmt.Println(result)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment