PLACE |
spec |
solutions |
Go |
The first thing the main function of this implementation does is creating the stdin
data object (a reader) capable of performing reading operations from the standard input. That stdin
then is used for repeatedly calling the library function ReadString
to obtain the input for the calculator. ReadString
returns a pair where the second member is an error value; eventually the error becomes non-nil
, signalling end of input and leading to terminating the program.
ReadString
is a general procedure which we instruct to read complete lines by telling it to stop at \n
. The \n
itself is also stored in the read string s
. A sequence of non-blank tokens is obtained from s
through a call to Fields
. (The latter drops all whitespace, i.e. spaces, tab characters, and the newline character.) If the token sequence is non-empty, it is passed to evalrpn
for evaluating the RPN expression.
The evaluation is carried out by running a for
loop over the slice of tokens tks
and maintaining a stack of intermediate results within the slice nums
. The pop
variable holds a function for pulling a value off the stack, and the built-in function append
pushes a number onto it.
If a token tk
is the name of an arithmetic operator, two numbers are popped off nums
and the respective operation is performed on them. Otherwise the program attempts reading tk
as a (64-bit) floating-point number. ParseFloat
returns a two-value result with the second item representing an error if not nil
.
Syntax errors in the RPN expression are handled in evalrpn
through utilizing the defer
-panic
-recover
mechanism. The program ‘panics’ explicitly, by calling the eponymous function upon encountering an erroneous token. It also raises a ‘panic’ situation implicitly, when pop
is called with an empty stack: nums[-1]
is then an invalid array/slice reference. In such an exceptional situation, control leaves (pop
and) evalrpn
immediately.
Regardless of whether a ‘panic’ has been raised or not, the defer
red anonymous function call is executed on leaving evalrpn
, before the control gets passed back to main
. Within that call, if there was no ‘panic’ (recover
returns nil
) and there is only a single number in nums
, the RPN expression is finally proven correct so the obtained result x
is printed. Otherwise the expression is syntactically incorrect and an error message is duly printed.
Several standard library packages are import
ed for use in the program. Throughout the text, the names of the packages prefix the names of the functions that they provide to the program.
package main import ("os"; "bufio"; "fmt"; "strings"; "strconv") func evalrpn(tks []string) { var (nums []float64; x float64) pop := func() float64 { n := len(nums)-1 t := nums[n] nums = nums[:n] return t } defer func() {if recover()==nil && len(nums)==1 { fmt.Println(x)} else {fmt.Println("error")}}() for _,tk := range tks { switch tk { case "+": x = pop()+pop() case "*": x = pop()*pop() case "-": x = pop(); x = pop()-x case "/": x = pop(); x = pop()/x default: var e error x,e = strconv.ParseFloat(tk,64) if e!=nil {panic(0)} } nums = append(nums,x) } } func main() { stdin := bufio.NewReader(os.Stdin) for { s,e := stdin.ReadString('\n') if e!=nil {break} if tks := strings.Fields(s); len(tks)>0 {evalrpn(tks)} } }
boykobbatgmaildotcom