基于Go语言实现简单的计算器
计算器实现原理
我们平时见到的算式都是这种类型 1+(2+3)*4
,这种类型的表达式也被称为中缀表达式,我们很容易理解它的运算顺序。但是计算机却无法理解这个式子
因此我们需要将其转化为便于计算机理解的式子,转化为后缀表达式或者前缀表达式(其实都差不多)。在这里我们以后缀表达式为例子。
中缀表达式如何转化为后缀表达式
1+(2+3)*4
的后缀表达式为123+4*+
,前者转换成后者的过程需要利用到栈和队列这两个数据结构,不太清楚可以看看:栈和队列详解
首先我们需要一个栈和队列
然后从左到右依次根据一定规则判断是否入栈
入栈规则如下:
- 数字直接入队列
- 若是运算符,则判断其与栈顶符号的优先级,优先级低于或等于栈顶符号,栈内元素不断出栈,进入队列,直到栈空或者碰见左括号为止
- 若是左括号则直接入栈
- 若是右括号则栈内所有元素出栈,进入队列,直到遇见与之匹配的左括号
- 最后栈内所有元素按顺序入列
现在我们开始进行变换
最后得到我们的结果123+4*+
中缀就成功转化成后缀表达式了
计算机是如何理解后缀表达式的
计算机会将之前放在队列里的元素按照先进先出(FIFO)的规则,将元素弹出进行判断
如果元素为数字,则直接入栈,若元素为运算符,则从栈中弹出两个数字进行运算,再将运算结果放入栈中
当队列全部元素取出后,最后栈中剩下的唯一一个元素就是我们要找的结果了
在GO中的实现
首先我们需要创造出我们的工具:栈和队列
实现栈
type Stack struct { //定义栈 elements []interface{} //因为储存的元素是空接口类型,所以之后要注意类型断言和类型转化 } func NewStack() *Stack { //返回一个栈 return &Stack{} } func (s *Stack) empty() bool { //判断栈是否为空 return len(s.elements) == 0 } func (s *Stack) push(x interface{}) { //将元素入栈 s.elements = append(s.elements, x) } func (s *Stack) pop() (interface{}, error) { //将栈顶元素记录并弹出 if s.empty() { return nil, errors.New("empty stack") } ret := s.elements[len(s.elements)-1] s.elements = s.elements[:len(s.elements)-1] return ret, nil } func (s *Stack) top() (interface{}, error) { //只查询栈顶元素,不弹出 if s.empty() { return nil, errors.New("empty stack") } return s.elements[len(s.elements)-1], nil }
实现队列
type Queue struct { //定义队列 elements []interface{} } func NewQueue() *Queue { //返回一个队列 return &Queue{} } func (q *Queue) empty() bool { //判断队列是否为空 return len(q.elements) == 0 } func (q *Queue) push(x interface{}) { //将元素压入队列 q.elements = append(q.elements, x) } func (q *Queue) pop() (interface{}, error) { //将最先进入的元素记录并弹出 if q.empty() { return nil, errors.New("empty queue") } if len(q.elements) == 1 { ret := q.elements[0] r := ret.(string) println(r) q.elements = q.elements[0:0] return ret, nil } else { ret := q.elements[0] q.elements = q.elements[1 : len(q.elements)-1] return ret, nil } }
中缀转后缀实现
按照先前的规则,灵活运用判断语句实现中缀到后缀表达式的实现
func Transform(S *Stack, Q *Queue, input string) error { temp := "" for i := 0; i < len(input); i++ { switch string(input[i]) { case "+": //前面有数字堆着就先让数字入列 if temp != "" { Q.push(temp) temp = "" } if S.empty() { //如果栈为空,直接入栈 S.push(string(input[i])) } else { // 如果栈不为空 m, _ := S.top() if m.(string) == "(" { //前一个是左括号直接入栈 S.push(string(input[i])) } else { //否则全出 for { t, _ := S.pop() Q.push(t.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" { //直到栈为空或者碰到左括号为止 break } } S.push(string(input[i])) } } case "-": //前面有数字堆着就先让数字入列 if temp != "" { Q.push(temp) temp = "" } if S.empty() { //如果栈为空,直接入栈 S.push(string(input[i])) } else { // 如果栈不为空 m, _ := S.top() if m.(string) == "(" { //前一个是左括号直接入栈 S.push(string(input[i])) } else { //否则全出 for { t, _ := S.pop() Q.push(t.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" { //直到栈为空或者碰到左括号为止 break } } S.push(string(input[i])) } } case "*": //前面有数字堆着就先让数字入列 if temp != "" { Q.push(temp) temp = "" } if S.empty() { //如果栈为空直接入栈 S.push(string(input[i])) } else { //反之,将栈内元素弹出,放入队列 t, _ := S.top() if t.(string) == "+" || t.(string) == "-" || t.(string) == "(" { //栈顶为加减号或左括号,直接入栈 S.push(string(input[i])) } else { for { j, _ := S.pop() Q.push(j.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" || a.(string) == "+" || a.(string) == "-" { //直到栈为空或者碰到左括号为止 break } } S.push(string(input[i])) } } case "/": //前面有数字堆着就先让数字入列 if temp != "" { Q.push(temp) temp = "" } if S.empty() { //如果栈为空直接入栈 S.push(string(input[i])) } else { //反之,将栈内元素弹出,放入队列 t, _ := S.top() if t.(string) == "+" || t.(string) == "-" || t.(string) == "(" { //栈顶为加减号或左括号,直接入栈 S.push(string(input[i])) } else { for { j, _ := S.pop() Q.push(j.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" || a.(string) == "+" || a.(string) == "-" { //直到栈为空或者碰到左括号为止 break } } S.push(string(input[i])) } } case "(": //别管,直接入栈 S.push(string(input[i])) case ")": //前面有数字堆着就先让数字入列 if temp != "" { Q.push(temp) temp = "" } for { j, _ := S.pop() Q.push(j.(string)) a, _ := S.top() if a.(string) == "(" { //直到碰到左括号为止,然后带走左括号 _, _ = S.pop() break } } default: if '0' <= input[i] && input[i] <= '9' { temp += string(input[i]) } else { return errors.New("valid input") } } } //若还有数字没有入队列就入 if temp != "" { Q.push(temp) } //若栈还有运算符就出栈 for { if S.empty() { break } t, _ := S.pop() Q.push(t.(string)) } return nil }
计算过程的实现
逻辑十分简单,主要注意的是类型间的转化,要从空接口类型断言为string类型,再将string类型转化为int类型进行计算,使用float类型也可以实现小数计算,可以自己去尝试
func Calculate(S *Stack, Q *Queue) int { for i := 0; i < len(Q.elements); i++ { t := Q.elements[i].(string) switch t { case "+": interNum1, _ := S.pop() interNum2, _ := S.pop() num1 := InterToNum(interNum1) num2 := InterToNum(interNum2) ret := num2 + num1 ret1 := strconv.Itoa(ret) S.push(ret1) case "-": interNum1, _ := S.pop() interNum2, _ := S.pop() num1 := InterToNum(interNum1) num2 := InterToNum(interNum2) ret := num2 - num1 ret1 := strconv.Itoa(ret) S.push(ret1) case "*": interNum1, _ := S.pop() interNum2, _ := S.pop() num1 := InterToNum(interNum1) num2 := InterToNum(interNum2) ret := num2 * num1 ret1 := strconv.Itoa(ret) S.push(ret1) case "/": interNum1, _ := S.pop() interNum2, _ := S.pop() num1 := InterToNum(interNum1) num2 := InterToNum(interNum2) ret := num2 / num1 ret1 := strconv.Itoa(ret) S.push(ret1) default: S.push(t) } } i, _ := S.pop() return InterToNum(i) }
源代码
package main import ( "bufio" "errors" "fmt" "os" "strconv" ) // Stack 实现栈 type Stack struct { elements []interface{} } func NewStack() *Stack { return &Stack{} } func (s *Stack) empty() bool { return len(s.elements) == 0 } func (s *Stack) push(x interface{}) { s.elements = append(s.elements, x) } func (s *Stack) pop() (interface{}, error) { if s.empty() { return nil, errors.New("empty stack") } ret := s.elements[len(s.elements)-1] s.elements = s.elements[:len(s.elements)-1] return ret, nil } func (s *Stack) top() (interface{}, error) { if s.empty() { return nil, errors.New("empty stack") } return s.elements[len(s.elements)-1], nil } // Queue 实现队列 type Queue struct { elements []interface{} } func NewQueue() *Queue { return &Queue{} } func (q *Queue) empty() bool { return len(q.elements) == 0 } func (q *Queue) push(x interface{}) { q.elements = append(q.elements, x) } func (q *Queue) pop() (interface{}, error) { if q.empty() { return nil, errors.New("empty queue") } if len(q.elements) == 1 { ret := q.elements[0] r := ret.(string) println(r) q.elements = q.elements[0:0] return ret, nil } else { ret := q.elements[0] q.elements = q.elements[1 : len(q.elements)-1] return ret, nil } } func InterToNum(i interface{}) int { str := i.(string) ret, _ := strconv.Atoi(str) return ret } func Transform(S *Stack, Q *Queue, input string) error { temp := "" for i := 0; i < len(input); i++ { switch string(input[i]) { case "+": //前面有数字堆着就先让数字入列 if temp != "" { Q.push(temp) temp = "" } if S.empty() { //如果栈为空,直接入栈 S.push(string(input[i])) } else { // 如果栈不为空 m, _ := S.top() if m.(string) == "(" { //前一个是左括号直接入栈 S.push(string(input[i])) } else { //否则全出 for { t, _ := S.pop() Q.push(t.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" { //直到栈为空或者碰到左括号为止 break } } S.push(string(input[i])) } } case "-": //前面有数字堆着就先让数字入列 if temp != "" { Q.push(temp) temp = "" } if S.empty() { //如果栈为空,直接入栈 S.push(string(input[i])) } else { // 如果栈不为空 m, _ := S.top() if m.(string) == "(" { //前一个是左括号直接入栈 S.push(string(input[i])) } else { //否则全出 for { t, _ := S.pop() Q.push(t.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" { //直到栈为空或者碰到左括号为止 break } } S.push(string(input[i])) } } case "*": //前面有数字堆着就先让数字入列 if temp != "" { Q.push(temp) temp = "" } if S.empty() { //如果栈为空直接入栈 S.push(string(input[i])) } else { //反之,将栈内元素弹出,放入队列 t, _ := S.top() if t.(string) == "+" || t.(string) == "-" || t.(string) == "(" { //栈顶为加减号或左括号,直接入栈 S.push(string(input[i])) } else { for { j, _ := S.pop() Q.push(j.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" || a.(string) == "+" || a.(string) == "-" { //直到栈为空或者碰到左括号为止 break } } S.push(string(input[i])) } } case "/": //前面有数字堆着就先让数字入列 if temp != "" { Q.push(temp) temp = "" } if S.empty() { //如果栈为空直接入栈 S.push(string(input[i])) } else { //反之,将栈内元素弹出,放入队列 t, _ := S.top() if t.(string) == "+" || t.(string) == "-" || t.(string) == "(" { //栈顶为加减号或左括号,直接入栈 S.push(string(input[i])) } else { for { j, _ := S.pop() Q.push(j.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" || a.(string) == "+" || a.(string) == "-" { //直到栈为空或者碰到左括号为止 break } } S.push(string(input[i])) } } case "(": //别管,直接入栈 S.push(string(input[i])) case ")": //前面有数字堆着就先让数字入列 if temp != "" { Q.push(temp) temp = "" } for { j, _ := S.pop() Q.push(j.(string)) a, _ := S.top() if a.(string) == "(" { //直到碰到左括号为止,然后带走左括号 _, _ = S.pop() break } } default: if '0' <= input[i] && input[i] <= '9' { temp += string(input[i]) } else { return errors.New("valid input") } } } //若还有数字没有入队列就入 if temp != "" { Q.push(temp) } //若栈还有运算符就出栈 for { if S.empty() { break } t, _ := S.pop() Q.push(t.(string)) } return nil } func Calculate(S *Stack, Q *Queue) int { for i := 0; i < len(Q.elements); i++ { t := Q.elements[i].(string) switch t { case "+": interNum1, _ := S.pop() interNum2, _ := S.pop() num1 := InterToNum(interNum1) num2 := InterToNum(interNum2) ret := num2 + num1 ret1 := strconv.Itoa(ret) S.push(ret1) case "-": interNum1, _ := S.pop() interNum2, _ := S.pop() num1 := InterToNum(interNum1) num2 := InterToNum(interNum2) ret := num2 - num1 ret1 := strconv.Itoa(ret) S.push(ret1) case "*": interNum1, _ := S.pop() interNum2, _ := S.pop() num1 := InterToNum(interNum1) num2 := InterToNum(interNum2) ret := num2 * num1 ret1 := strconv.Itoa(ret) S.push(ret1) case "/": interNum1, _ := S.pop() interNum2, _ := S.pop() num1 := InterToNum(interNum1) num2 := InterToNum(interNum2) ret := num2 / num1 ret1 := strconv.Itoa(ret) S.push(ret1) default: S.push(t) } } i, _ := S.pop() return InterToNum(i) } func main() { fmt.Println("输入规则:") fmt.Println("1.可输入加减乘除以及小括号") fmt.Println("2.只能输入正整数") fmt.Println("3.输入exit退出") for { fmt.Printf("请输入:") scanner := bufio.NewScanner(os.Stdin) scanner.Scan() // 读取输入内容,直到遇到换行符(包括空格) input := scanner.Text() if input == "exit" { break } S := NewStack() Q := NewQueue() err := Transform(S, Q, input) if err != nil { fmt.Println(err) } ret := Calculate(S, Q) fmt.Println("结果为: ", ret) } }
改进
上文只实现了正整数之间的加减乘除和小括号的运算,图方便未考虑其他可左右运算顺序的符号如:[]中括号 {}大括号 %取余,除此之外还可以尝试一下实现输入负数时处理的方法
以上就是基于Go语言实现简单的计算器的详细内容,更多关于go计算器的资料请关注脚本之家其它相关文章!
最新评论