Created
September 13, 2018 08:51
-
-
Save njdart/7a741063db5ccf14cfe6a553b25a2cfb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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