基于Go語言實現(xiàn)簡單的計算器
計算器實現(xiàn)原理
我們平時見到的算式都是這種類型 1+(2+3)*4,這種類型的表達式也被稱為中綴表達式,我們很容易理解它的運算順序。但是計算機卻無法理解這個式子
因此我們需要將其轉(zhuǎn)化為便于計算機理解的式子,轉(zhuǎn)化為后綴表達式或者前綴表達式(其實都差不多)。在這里我們以后綴表達式為例子。
中綴表達式如何轉(zhuǎn)化為后綴表達式
1+(2+3)*4的后綴表達式為123+4*+,前者轉(zhuǎn)換成后者的過程需要利用到棧和隊列這兩個數(shù)據(jù)結(jié)構(gòu),不太清楚可以看看:棧和隊列詳解
首先我們需要一個棧和隊列

然后從左到右依次根據(jù)一定規(guī)則判斷是否入棧
入棧規(guī)則如下:
- 數(shù)字直接入隊列
- 若是運算符,則判斷其與棧頂符號的優(yōu)先級,優(yōu)先級低于或等于棧頂符號,棧內(nèi)元素不斷出棧,進入隊列,直到??栈蛘吲鲆娮罄ㄌ枮橹?/li>
- 若是左括號則直接入棧
- 若是右括號則棧內(nèi)所有元素出棧,進入隊列,直到遇見與之匹配的左括號
- 最后棧內(nèi)所有元素按順序入列
現(xiàn)在我們開始進行變換






最后得到我們的結(jié)果123+4*+中綴就成功轉(zhuǎn)化成后綴表達式了
計算機是如何理解后綴表達式的
計算機會將之前放在隊列里的元素按照先進先出(FIFO)的規(guī)則,將元素彈出進行判斷
如果元素為數(shù)字,則直接入棧,若元素為運算符,則從棧中彈出兩個數(shù)字進行運算,再將運算結(jié)果放入棧中
當隊列全部元素取出后,最后棧中剩下的唯一一個元素就是我們要找的結(jié)果了
在GO中的實現(xiàn)
首先我們需要創(chuàng)造出我們的工具:棧和隊列
實現(xiàn)棧
type Stack struct { //定義棧
elements []interface{} //因為儲存的元素是空接口類型,所以之后要注意類型斷言和類型轉(zhuǎn)化
}
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
}
實現(xiàn)隊列
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
}
}
中綴轉(zhuǎn)后綴實現(xiàn)
按照先前的規(guī)則,靈活運用判斷語句實現(xiàn)中綴到后綴表達式的實現(xiàn)
func Transform(S *Stack, Q *Queue, input string) error {
temp := ""
for i := 0; i < len(input); i++ {
switch string(input[i]) {
case "+":
//前面有數(shù)字堆著就先讓數(shù)字入列
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 "-":
//前面有數(shù)字堆著就先讓數(shù)字入列
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 "*":
//前面有數(shù)字堆著就先讓數(shù)字入列
if temp != "" {
Q.push(temp)
temp = ""
}
if S.empty() { //如果棧為空直接入棧
S.push(string(input[i]))
} else { //反之,將棧內(nèi)元素彈出,放入隊列
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 "/":
//前面有數(shù)字堆著就先讓數(shù)字入列
if temp != "" {
Q.push(temp)
temp = ""
}
if S.empty() { //如果棧為空直接入棧
S.push(string(input[i]))
} else { //反之,將棧內(nèi)元素彈出,放入隊列
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 ")":
//前面有數(shù)字堆著就先讓數(shù)字入列
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")
}
}
}
//若還有數(shù)字沒有入隊列就入
if temp != "" {
Q.push(temp)
}
//若棧還有運算符就出棧
for {
if S.empty() {
break
}
t, _ := S.pop()
Q.push(t.(string))
}
return nil
}
計算過程的實現(xiàn)
邏輯十分簡單,主要注意的是類型間的轉(zhuǎn)化,要從空接口類型斷言為string類型,再將string類型轉(zhuǎn)化為int類型進行計算,使用float類型也可以實現(xiàn)小數(shù)計算,可以自己去嘗試
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 實現(xiàn)棧
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 實現(xiàn)隊列
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 "+":
//前面有數(shù)字堆著就先讓數(shù)字入列
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 "-":
//前面有數(shù)字堆著就先讓數(shù)字入列
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 "*":
//前面有數(shù)字堆著就先讓數(shù)字入列
if temp != "" {
Q.push(temp)
temp = ""
}
if S.empty() { //如果棧為空直接入棧
S.push(string(input[i]))
} else { //反之,將棧內(nèi)元素彈出,放入隊列
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 "/":
//前面有數(shù)字堆著就先讓數(shù)字入列
if temp != "" {
Q.push(temp)
temp = ""
}
if S.empty() { //如果棧為空直接入棧
S.push(string(input[i]))
} else { //反之,將棧內(nèi)元素彈出,放入隊列
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 ")":
//前面有數(shù)字堆著就先讓數(shù)字入列
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")
}
}
}
//若還有數(shù)字沒有入隊列就入
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("輸入規(guī)則:")
fmt.Println("1.可輸入加減乘除以及小括號")
fmt.Println("2.只能輸入正整數(shù)")
fmt.Println("3.輸入exit退出")
for {
fmt.Printf("請輸入:")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan() // 讀取輸入內(nèi)容,直到遇到換行符(包括空格)
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("結(jié)果為: ", ret)
}
}
改進
上文只實現(xiàn)了正整數(shù)之間的加減乘除和小括號的運算,圖方便未考慮其他可左右運算順序的符號如:[]中括號 {}大括號 %取余,除此之外還可以嘗試一下實現(xiàn)輸入負數(shù)時處理的方法
以上就是基于Go語言實現(xiàn)簡單的計算器的詳細內(nèi)容,更多關(guān)于go計算器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
go?module化?import?調(diào)用本地模塊?tidy的方法
這篇文章主要介紹了go?module化?import?調(diào)用本地模塊?tidy的相關(guān)知識,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09
Golang網(wǎng)絡(luò)模型netpoll源碼解析(具體流程)
本文介紹了Golang的網(wǎng)絡(luò)模型netpoll的實現(xiàn)原理,本文將從為什么需要使用netpoll模型,以及netpoll的具體流程實現(xiàn)兩個主要角度來展開學(xué)習(xí),感興趣的朋友跟隨小編一起看看吧2024-11-11
golang?db事務(wù)的統(tǒng)一封裝的實現(xiàn)
這篇文章主要介紹了golang db事務(wù)的統(tǒng)一封裝的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12
Go垃圾回收提升內(nèi)存管理效率優(yōu)化最佳實踐
這篇文章主要為大家介紹了Go垃圾回收提升內(nèi)存管理效率優(yōu)化最佳實踐,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12

