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 中缀表达式转前缀表达式
这是从别人那拷贝哒~,就没有举例了。参考:前缀、中缀、后缀表达式(逆波兰表达式)
- 初始化两个栈:运算符栈s1,储存中间结果的栈s2
- 从右至左扫描中缀表达式
- 遇到操作数时,将其压入s2
- 遇到运算符时,比较其与s1栈顶运算符的优先级
- 如果s1为空,或栈顶运算符为右括号“)”,则直接将此运算符入栈
- 否则,若优先级比栈顶运算符的较高或相等,也将运算符压入s1
- 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较
- 遇到括号时
- 如果是右括号“)”,则直接压入s1
- 如果是左括号“(”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到右括号为止,此时将这一对括号丢弃
- 重复步骤2至5,直到表达式的最左边
- 将s1中剩余的运算符依次弹出并压入s2
- 依次弹出s2中的元素并输出,结果即为中缀表达式对应的前缀表达式
2 后缀表达式转前缀表达式
这是自己的。
从左至右扫描中缀表达式:
若读取的是操作数,则将该操作数存入后缀表达式。
若读取的是运算符:
(1) 该运算符为左括号"(",则直接压入运算符堆栈。
(2) 该运算符为右括号")",则输出运算符堆栈中的运算符到后缀表达式,直到遇到左括号为止。
(3) 该运算符为非括号运算符:
(a)若运算符堆栈为空,则直接压入运算符堆栈。
(b)若运算符堆栈栈顶的运算符为括号,则直接压入运算符堆栈。
©若比运算符堆栈栈顶的运算符优先级低或相等,则输出栈顶运算符到后缀表达式,直接找到一个栈顶运算符的优先级大于当前运算符,或者栈为空。并将当前运算符压入运算符堆栈。
(d)若比运算符堆栈栈顶的运算符优先级高,则直接压入运算符堆栈。扫描结束,将运算符堆栈中的运算符依次弹出,存入后缀表达式。
举例:
例如中缀表达式: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
}