golang-支持括号运算的计算器

核能气质少年 提交于 2021-02-17 07:20:50

现在需要实现一个计算器函数,输入是一个字符串格式的数学运算表达式,要求计算出该表达式的最终结果。表达式的操作符支持加减乘除和小括号。
思路:利用stack,将低优先级的运算压栈,遇到相同优先级的则计算第一个符号。右括号的,把括号内式子单独计算,并把结果代入原式子继续计算

package main

import (
	"errors"
	"fmt"
	"strconv"
	"log"
)

type Stack struct {
	elements []interface{} //elements
}

func NewStack() *Stack {
	return &Stack{make([]interface{}, 0, 100)}
}

func (s *Stack) Push(value ...interface{}) {
	s.elements = append(s.elements, value...)
}

//返回下一个元素
func (s *Stack) Top() (value interface{}) {
	if s.Size() > 0 {
		return s.elements[s.Size()-1]
	}
	return nil //read empty s
}

//返回下一个元素,并从Stack移除元素
func (s *Stack) Pop() (value interface{}, err error) {
	if s.Size() > 0 {
		value = s.elements[s.Size()-1]
		s.elements = s.elements[:s.Size()-1]
		return
	}
	return nil, errors.New("s is empty") //read empty s
}

//Stack的size
func (s *Stack) Size() (int) {
	return len(s.elements)
}

//是否为空
func (s *Stack) Empty() (bool) {
	if s.elements == nil || s.Size() == 0 {
		return true
	}
	return false
}

//打印
func (s *Stack) Print() {
	for i := len(s.elements) - 1; i >= 0; i-- {
		fmt.Println(i, "=>", s.elements[i])
	}
}

type Calculator struct {
	stDat *Stack
	stSym *Stack
}

func NewCalculator() *Calculator {
	return &Calculator{NewStack(), NewStack()}
}

func math(num1, num2 float64, sym byte) (result float64) {
	switch sym {
	case '+':
		result = num1 + num2
	case '-':
		result = num1 - num2
	case '*':
		result = num1 * num2
	case '/':
		result = num1 / num2
	}
	//fmt.Println("math result:", result)
	return
}

func (c *Calculator) Step(num float64, sym byte) error {
	// 判断栈顶符合与当前符号运算优先级。
	// 如果sym不高于栈顶符号,则取出数据栈顶数字、符号栈顶符号 与 num 运算,并将结果压入数据栈,sym压入符号栈。
	// 如果sym优先级更高,则分别将 num, sym 压入数据栈和符号栈。
	// 如果 sym = '\n' 表示算式结束,依次出栈运算。
	if sym == '\n' {
		if c.stSym.Size() == 0 {
			c.stDat.Push(num)
		} else {
			length := c.stSym.Size()
			for i:=0; i < length; i++ {
				symbol, _ := c.stSym.Pop()
				ifc, _ := c.stDat.Pop()
				num1 := ifc.(float64)
				num = math(num1, num, symbol.(byte))
			}
			c.stDat.Push(num)
		}
	} else {
		if c.stSym.Size() != 0 {
			tSymbol := c.stSym.Top().(byte)
			pc := priorityCompare(tSymbol, sym)
			switch {
			case pc < 0:
				c.stDat.Push(num)
				c.stSym.Push(sym)
			case pc >= 0:
				c.stSym.Pop()
				ifc, _ := c.stDat.Pop()

				num1 := ifc.(float64)
				c.stDat.Push(math(num1, num, tSymbol))
				c.stSym.Push(sym)
			}
		} else {
			c.stDat.Push(num)
			c.stSym.Push(sym)
		}
	}
	return nil
}

func (c *Calculator) Result() (result float64, err error) {
	if c.stSym.Empty() && c.stDat.Size() == 1 {
		result = c.stDat.Top().(float64)
	} else {
		//计算式未结束或者计算式有误
		err = errors.New("计算式未结束或者计算式有误")
	}
	return
}

// 比较运算符优先级,return >0,则s1高于s2; =0,则s1、s2相同; <0,则s1低于s2
func priorityCompare(s1, s2 byte) int {
	level := func(sym byte) int {
		lvl := 0
		switch sym {
		case '+', '-':
			lvl = 1
		case '*', '/':
			lvl = 2
		}
		return lvl
	}

	return level(s1) - level(s2)
}

func Calculate(str []byte) (result float64, err error) {
	fmt.Println("str:", string(str))
	cal := NewCalculator()
	sNum, num := make([]byte, 0, 100), 0.0

	for idx := 0; idx < len(str); idx++ {
		c := str[idx]
		switch c {
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			sNum = append(sNum, c)

		case '+', '-', '*', '/':
			if (len(sNum) > 0 && num != 0.0) || (len(sNum) == 0 && num == 0.0) {
				err = errors.New("计算式有误")
				return
			}

			if len(sNum) > 0 {
				strNum := string(sNum)
				intNum, _ := strconv.Atoi(strNum)
				num = float64(intNum)
			}
			cal.Step(num, c)
			sNum, num = make([]byte, 0, 100), 0.0
		case '(':
			idx++
		Loop:
			for j := idx; j < len(str); j++ {
				if str[j] == ')' {
					num, err = Calculate(str[idx:j])
					if err != nil {
						return
					}
					idx = j
					break Loop
				}
			}
		default:
			err = errors.New(fmt.Sprintf("无效符号: %s", c))
			return
		}
	}

	// 扫描结束
	if (len(sNum) > 0 && num != 0.0) || (len(sNum) == 0 && num == 0.0) {
		err = errors.New("计算式有误")
		return
	}

	if len(sNum) > 0 {
		strNum := string(sNum)
		intNum, _ := strconv.Atoi(strNum)
		num = float64(intNum)
	}
	cal.Step(num, '\n')
	return cal.Result()
}

func main() {
	express := []byte("123+(3+5*4)+4-(2*5+6*3/2)")
	//express := []byte("3+5")
	result, err := Calculate(express)
	if err != nil {
		log.Fatalln(err)
	}

	fmt.Println("result:", result)
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!