go语言实现四则运算

go语言实现四则运算


一 将四则运算用中缀表达式表示

前缀表达式、中缀表达式、后缀表达式都是四则运算的表示方式。为了按照四则运算的运算顺序求值(比如乘法、括号比加减运算级高),可以将四则运算用前缀或者后缀表达式表示,方便求值。

比如四则运算:9+(3-1)*3+10/2
前缀表达式:+ + 9 * - 3 1 3 / 10 2
中缀表达式:9+(3-1)*3+10/2
后缀表达式:9 3 1 - 3 * 10 2 / + +

前缀表达式又称波兰式前缀表达式的运算符位于操作数之前
后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后。

二 前缀、后缀表达式求值

1 前缀表达式求值

从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算( 栈顶元素 op 次栈顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果。

举例:

例如前缀表达式:+ + 9 * - 3 1 3 / 10 2
(1) 从右往左扫描到数字2,压栈
	stack: 2
(2) 扫描到数字10,压栈
	stack: 2 10
(3) 扫描到运算符/,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
	10 / 2 = 5
	stack: 5
(4) 扫描到数字3,压栈
	stack: 5 3
(5) 扫描到数字1,压栈
	stack: 5 3 1
(6) 扫描到数字3,压栈
	stack: 5 3 1 3
(7) 扫描到运算符-,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
	3 - 1 = 2
	stack: 5 3 2
(8) 扫描到运算符*,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
	2 * 3 = 6
	stack: 5 6
(9) 扫描到数字9,压栈
	stack: 5 6 9
(10) 扫描到运算符+,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
	9 + 6 = 15
	stack: 5 15
(11) 扫描到运算符+,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
	15 + 5 = 20
	stack: 20
(12) 最后运算得出的值即为表达式的结果
	result = 20

2 后缀表达式求值

从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算( 次栈顶元素 op 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果。

举例:

例如后缀表达式:9 3 1 - 3 * 10 2 / + +
(1) 从左往右扫描到数字9,压栈
	stack: 9
(2) 扫描到数字3,压栈
	stack: 9 3
(3) 扫描到数字1,压栈
	stack: 9 3 1
(4) 扫描到运算符-,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
	3 - 1 = 2
	stack: 9 2
(5) 扫描到数字3,压栈
	stack: 9 2 3
(6) 扫描到运算符*,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
	2 * 3 = 6
	stack: 9 6
(7) 扫描到数字10,压栈
	stack: 9 6 10
(8) 扫描到数字2,压栈
	stack: 9 6 10 2
(9) 扫描到运算符/,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
	10 / 2 = 5
	stack: 9 6 5
(10) 扫描到运算符+,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
	6 + 5 = 11
	stack: 9 11
(11) 扫描到运算符+,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
	9 + 11 = 20
	stack: 20
(12) 最后运算得出的值即为表达式的结果
	result = 20

三 中缀表达式转前、后缀表达式

1 中缀表达式转前缀表达式

这是从别人那拷贝哒~,就没有举例了。参考:前缀、中缀、后缀表达式(逆波兰表达式)

  1. 初始化两个栈:运算符栈s1,储存中间结果的栈s2
  2. 从右至左扫描中缀表达式
  3. 遇到操作数时,将其压入s2
  4. 遇到运算符时,比较其与s1栈顶运算符的优先级
    1. 如果s1为空,或栈顶运算符为右括号“)”,则直接将此运算符入栈
    2. 否则,若优先级比栈顶运算符的较高或相等,也将运算符压入s1
    3. 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较
  5. 遇到括号时
    1. 如果是右括号“)”,则直接压入s1
    2. 如果是左括号“(”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到右括号为止,此时将这一对括号丢弃
  6. 重复步骤2至5,直到表达式的最左边
  7. 将s1中剩余的运算符依次弹出并压入s2
  8. 依次弹出s2中的元素并输出,结果即为中缀表达式对应的前缀表达式

2 后缀表达式转前缀表达式

这是自己的。

从左至右扫描中缀表达式:

  1. 若读取的是操作数,则将该操作数存入后缀表达式。

  2. 若读取的是运算符:
    (1) 该运算符为左括号"(",则直接压入运算符堆栈。
    (2) 该运算符为右括号")",则输出运算符堆栈中的运算符到后缀表达式,直到遇到左括号为止。
    (3) 该运算符为非括号运算符:
     (a)若运算符堆栈为空,则直接压入运算符堆栈。
     (b)若运算符堆栈栈顶的运算符为括号,则直接压入运算符堆栈。
     ©若比运算符堆栈栈顶的运算符优先级低或相等,则输出栈顶运算符到后缀表达式,直接找到一个栈顶运算符的优先级大于当前运算符,或者栈为空。并将当前运算符压入运算符堆栈。
     (d)若比运算符堆栈栈顶的运算符优先级高,则直接压入运算符堆栈。

  3. 扫描结束,将运算符堆栈中的运算符依次弹出,存入后缀表达式。

举例:

例如中缀表达式:9+(3-1)*3+10/2
(1) 从左往右扫描到数字9,将该操作数存入后缀表达式
	postfixExpress(后缀表达式):9
(2) 扫描到"+",运算符堆栈为空,压入运算符堆栈
	postfixExpress:9	stack:+
(3) 扫描到左括号"(",压入运算符堆栈
	postfixExpress:9	stack:+ (
(4) 扫描到数字3,将该操作数存入后缀表达式
	postfixExpress:9 3 	stack:+ (
(5) 扫描到"-",运算符堆栈栈顶的运算符为括号,压入运算符堆栈
	postfixExpress:9 3 	stack:+ ( -
(6) 扫描到数字1,将该操作数存入后缀表达式
	postfixExpress:9 3 1	stack:+ ( -
(7) 扫描到右括号")",则输出运算符堆栈中的运算符到后缀表达式,直到遇到左括号为止
	postfixExpress:9 3 1 - 	stack:+
(8) 扫描到"*",比运算符堆栈栈顶的运算符优先级高,压入运算符堆栈
	postfixExpress:9 3 1 - 	stack:+ *
(9) 扫描到数字3,将该操作数存入后缀表达式
	postfixExpress:9 3 1 - 3	stack:+ *
(10) 扫描到"+",比运算符堆栈栈顶的运算符优先级低,输出栈顶运算符到后缀表达式,并将当前运算符压入运算符堆栈
	postfixExpress:9 3 1 - 3 *		stack:+ +
(11) 扫描到数字10,将该操作数存入后缀表达式
		postfixExpress:9 3 1 - 3 *	10		stack:+ +
(12) 扫描到"/",比运算符堆栈栈顶的运算符优先级高,压入运算符堆栈
	postfixExpress:9 3 1 - 3 *	10		stack:+ + /
(13) 扫描到数字2,将该操作数存入后缀表达式
		postfixExpress:9 3 1 - 3 *	10 2	stack:+ + /
(14) 扫描结束,将运算符堆栈中的运算符依次弹出,存入后缀表达式
		postfixExpress:9 3 1 - 3 *	10 2 / + +

四 GO代码

main.go

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	process()
}

//	输入一个四则运算表达式(中缀表达式),求出其结果。如:9+(3-1)*3+10/2,结果为 20。
func process() (err error) {
	for {
		//	输入
		express,_ := getInput()
		if len(express) == 0 {
			continue
		}

		//	将中缀表达式转换成后缀表达式(逆波兰式),postfixExpress:后缀表达式
		express = strings.ReplaceAll(express, " ", "")
		postfixExpress,errRet := transPostfixExpress(express)
		if errRet != nil {
			err = errRet
			fmt.Println(errRet)
			return
		}
		//fmt.Println(postfixExpress)

		//	后缀表达式求值
		result,errRet := calc(postfixExpress)
		if errRet != nil {
			fmt.Println("error:",errRet)
			continue
		}
		fmt.Println(result)
	}
	return
}

func getInput() (string, error) {
	reader := bufio.NewReader(os.Stdin)
	return reader.ReadString('\n')
}

func priority(s byte) int {
	switch s {
	case '+':
		return 1
	case '-':
		return 1
	case '*':
		return 2
	case '/':
		return 2
	}
	return 0
}

//	将中缀表达式转换成后缀表达式(逆波兰式),postfixExpress:后缀表达式
func transPostfixExpress(express string) (postfixExpress []string, err error) {
	var (
		opStack Stack	//	运算符堆栈
		i int
	)

	LABEL:
		for i < len(express) {	//	从左至右扫描中缀表达式
			switch  {
			//	1. 若读取的是操作数,则将该操作数存入后缀表达式。
			case express[i] >= '0' && express[i] <= '9':
				var number []byte	//	如数字123,由'1'、'2'、'3'组成
				for ; i < len(express); i++ {
					if express[i] < '0' || express[i] > '9' {
						break
					}
					number = append(number, express[i])
				}
				postfixExpress = append(postfixExpress, string(number))

			//	2. 若读取的是运算符:
			//	(1) 该运算符为左括号"(",则直接压入运算符堆栈。
			case express[i] == '(':
				opStack.Push(fmt.Sprintf("%c", express[i]))
				i++

			//	(2) 该运算符为右括号")",则输出运算符堆栈中的运算符到后缀表达式,直到遇到左括号为止。
			case express[i] == ')':
				for !opStack.IsEmpty() {
					data, _ := opStack.Pop()
					if data[0] == '(' {
						break
					}
					postfixExpress = append(postfixExpress, data)
				}
				i++

			//	(3) 该运算符为非括号运算符:
			case express[i] == '+' || express[i] == '-' || express[i] == '*' || express[i] == '/':
				//	(a)若运算符堆栈为空,则直接压入运算符堆栈。
				if opStack.IsEmpty() {
					opStack.Push(fmt.Sprintf("%c", express[i]))
					i++
					continue LABEL
				}

				data, _ := opStack.Top()
				//	(b)若运算符堆栈栈顶的运算符为括号,则直接压入运算符堆栈。(只可能为左括号这种情况)
				if data[0] == '(' {
					opStack.Push(fmt.Sprintf("%c", express[i]))
					i++
					continue LABEL
				}
				//	(c)若比运算符堆栈栈顶的运算符优先级低或相等,则输出栈顶运算符到后缀表达式,直到栈为空或者找到优先级高于当前运算符。并将当前运算符压入运算符堆栈。
				if priority(express[i]) <= priority(data[0]) {
				tmp := priority(express[i])
				for !opStack.IsEmpty() && tmp <= priority(data[0]) {
					postfixExpress = append(postfixExpress, data)
					opStack.Pop()
					data, _ = opStack.Top()
				}
				opStack.Push(fmt.Sprintf("%c", express[i]))
				i++
				continue LABEL
			}
				//	(d)若比运算符堆栈栈顶的运算符优先级高,则直接压入运算符堆栈。
				opStack.Push(fmt.Sprintf("%c", express[i]))
				i++

			default:
				err = fmt.Errorf("invalid express:%v", express[i])
				return
			}
		}

		//	3. 扫描结束,将运算符堆栈中的运算符依次弹出,存入后缀表达式。
		for !opStack.IsEmpty() {
			data, _ := opStack.Pop()
			if data[0] == '#' {
				break
			}
			postfixExpress = append(postfixExpress, data)
		}

		return
}

//	后缀表达式求值
func calc(postfixExpress []string) (result int64, err error) {
	var (
		num1 string
		num2 string
		s Stack		//	操作栈,用于存入操作数,运算符
	)

	//	从左至右扫描后缀表达式
	for i := 0; i < len(postfixExpress); i++ {
		var cur = postfixExpress[i]

		//	1. 若读取的是运算符
		if cur[0] == '+' || cur[0] == '-' || cur[0] == '*' || cur[0] == '/' {
			//	从操作栈中弹出两个数进行运算
			num1, err = s.Pop()
			if err != nil {
				return
			}
			num2, err = s.Pop()
			if err != nil {
				return
			}

			//	先弹出的数为B,后弹出的数为A
			B, _ := strconv.Atoi(num1)
			A, _ := strconv.Atoi(num2)
			var res int

			switch cur[0] {
			case '+':
				res = A + B
			case '-':
				res = A - B
			case '*':
				res = A * B
			case '/':
				res = A / B
			default:
				err = fmt.Errorf("invalid operation")
				return
			}

			//	将中间结果压栈
			s.Push(fmt.Sprintf("%d", res))
			//fmt.Println("mid value = ", res)
		} else {
			//	1. 若读取的是操作数,直接压栈
			s.Push(cur)
		}
	}

	//	计算结束,栈顶保存最后结果
	resultStr, err := s.Top()
	if err != nil {
		return
	}
	result, err = strconv.ParseInt(resultStr, 10, 64)
	return
}

stack.go

package main

import "fmt"

type Stack struct {
	data [1024]string
	top int
}

func (s *Stack) IsEmpty() bool {
	return s.top == 0
}

func (s *Stack) Top() (ret string, err error) {
	if s.top == 0 {
		err = fmt.Errorf("stack is empty")
		return
	}
	ret = s.data[s.top-1]
	return
}

func (s *Stack) Push(str string) {
	s.data[s.top] = str
	s.top++
}

func (s *Stack) Pop() (ret string, err error) {
	if s.top == 0 {
		err = fmt.Errorf("stack is empty")
		return
	}
	s.top--
	ret = s.data[s.top]
	return
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值