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("栈为空")
	}
	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("栈为空")
	}
	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("队列为空")
	}
	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 "+":
			//如果temp中有数字,就把其放入队列
			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 "-":
			//如果temp中有数字,就把其放入队列
			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 "*":
			//如果temp中有数字,就把其放入队列
			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 "/":
			//如果temp中有数字,就把其放入队列
			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 ")":
			//如果temp中有数字,就把其放入队列
			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("输入错误!")
			}
		}
	}
	//把最后一个数字放入队列
	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) float64 {
	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.FormatFloat(ret, 'f', 10, 64)
			S.push(ret1)
		case "-":
			interNum1, _ := S.pop()
			interNum2, _ := S.pop()
			num1 := InterToNum(interNum1)
			num2 := InterToNum(interNum2)
			ret := num2 - num1
			ret1 := strconv.FormatFloat(ret, 'f', 10, 64)
			S.push(ret1)
		case "*":
			interNum1, _ := S.pop()
			interNum2, _ := S.pop()
			num1 := InterToNum(interNum1)
			num2 := InterToNum(interNum2)
			ret := num2 * num1
			ret1 := strconv.FormatFloat(ret, 'f', 10, 64)
			S.push(ret1)
		case "/":
			interNum1, _ := S.pop()
			interNum2, _ := S.pop()
			num1 := InterToNum(interNum1)
			num2 := InterToNum(interNum2)
			ret := num2 / num1
			ret1 := strconv.FormatFloat(ret, 'f', 10, 64)
			S.push(ret1)
		default:
			S.push(t)
		}
	}
	i, _ := S.pop()
	return InterToNum(i)
}

捕获所有panic错误

defer func() {
			if r := recover(); r != nil {
				if err, ok := r.(string); ok {
					fmt.Println("捕获到错误:", err)
				} else {
					fmt.Println("当前格式不能进行运算")
				}
			}
		}()

判断正则表达式

// 判断输入的字符串是否仅含有0-9,一元运算符,左右小括号和小写字母n
func isValidExpression(s string) bool {
	re := regexp.MustCompile(`^[0-9+\-*/n()]+$`)
	return re.MatchString(s)
}

源代码

package main

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
)

// 实现栈
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("栈为空")
	}
	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("栈为空")
	}
	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("队列为空")
	}
	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{}) float64 {
	str := i.(string)
	ret, _ := strconv.ParseFloat(str, 10)
	return ret
}

// 把需要计算的式子转化为后缀表达式
func Transform(S *Stack, Q *Queue, input string) error {
	temp := ""
	for i := 0; i < len(input); i++ {
		switch string(input[i]) {
		case "+":
			//如果temp中有数字,就把其放入队列
			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 "-":
			//如果temp中有数字,就把其放入队列
			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 "*":
			//如果temp中有数字,就把其放入队列
			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 "/":
			//如果temp中有数字,就把其放入队列
			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 ")":
			//如果temp中有数字,就把其放入队列
			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("输入错误!")
			}
		}
	}
	//把最后一个数字放入队列
	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) float64 {
	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.FormatFloat(ret, 'f', 10, 64)
			S.push(ret1)
		case "-":
			interNum1, _ := S.pop()
			interNum2, _ := S.pop()
			num1 := InterToNum(interNum1)
			num2 := InterToNum(interNum2)
			ret := num2 - num1
			ret1 := strconv.FormatFloat(ret, 'f', 10, 64)
			S.push(ret1)
		case "*":
			interNum1, _ := S.pop()
			interNum2, _ := S.pop()
			num1 := InterToNum(interNum1)
			num2 := InterToNum(interNum2)
			ret := num2 * num1
			ret1 := strconv.FormatFloat(ret, 'f', 10, 64)
			S.push(ret1)
		case "/":
			interNum1, _ := S.pop()
			interNum2, _ := S.pop()
			num1 := InterToNum(interNum1)
			num2 := InterToNum(interNum2)
			ret := num2 / num1
			ret1 := strconv.FormatFloat(ret, 'f', 10, 64)
			S.push(ret1)
		default:
			S.push(t)
		}
	}
	i, _ := S.pop()
	return InterToNum(i)
}

// 判断输入的字符串是否仅含有0-9,一元运算符,左右小括号和小写字母n
func isValidExpression(s string) bool {
	re := regexp.MustCompile(`^[0-9+\-*/n()]+$`)
	return re.MatchString(s)
}

func main() {
	for {
		fmt.Println("如果输入n,则直接退出程序。")
		fmt.Printf("请输入:")
		var input string
		_, err01 := fmt.Scan(&input) //获取输入内容
		if err01 != nil {
			fmt.Println("输入错误,请重新输入!!!")
		}
		if isValidExpression(input) == false {
			fmt.Println("输入错误!!!")
			break
		}
		defer func() {
			if r := recover(); r != nil {
				if err, ok := r.(string); ok {
					fmt.Println("捕获到错误:", err)
				} else {
					fmt.Println("当前格式不能进行运算")
				}
			}
		}()
		if input == "n" {
			break
		}
		S := NewStack()
		Q := NewQueue()
		err := Transform(S, Q, input)
		if err != nil {
			fmt.Println(err)
		}
		ret := Calculate(S, Q)
		fmt.Println("结果为: ", ret)
	}
}

改进

上文只实现了正整数之间的加减乘除和小括号的运算,图方便未考虑其他可左右运算顺序的符号如:[]中括号 {}大括号 %取余,除此之外还可以尝试一下实现输入负数时处理的方法

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值