基于Go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器
計(jì)算器實(shí)現(xiàn)原理
我們平時(shí)見(jiàn)到的算式都是這種類型 1+(2+3)*4
,這種類型的表達(dá)式也被稱為中綴表達(dá)式,我們很容易理解它的運(yùn)算順序。但是計(jì)算機(jī)卻無(wú)法理解這個(gè)式子
因此我們需要將其轉(zhuǎn)化為便于計(jì)算機(jī)理解的式子,轉(zhuǎn)化為后綴表達(dá)式或者前綴表達(dá)式(其實(shí)都差不多)。在這里我們以后綴表達(dá)式為例子。
中綴表達(dá)式如何轉(zhuǎn)化為后綴表達(dá)式
1+(2+3)*4
的后綴表達(dá)式為123+4*+
,前者轉(zhuǎn)換成后者的過(guò)程需要利用到棧和隊(duì)列這兩個(gè)數(shù)據(jù)結(jié)構(gòu),不太清楚可以看看:棧和隊(duì)列詳解
首先我們需要一個(gè)棧和隊(duì)列
然后從左到右依次根據(jù)一定規(guī)則判斷是否入棧
入棧規(guī)則如下:
- 數(shù)字直接入隊(duì)列
- 若是運(yùn)算符,則判斷其與棧頂符號(hào)的優(yōu)先級(jí),優(yōu)先級(jí)低于或等于棧頂符號(hào),棧內(nèi)元素不斷出棧,進(jìn)入隊(duì)列,直到棧空或者碰見(jiàn)左括號(hào)為止
- 若是左括號(hào)則直接入棧
- 若是右括號(hào)則棧內(nèi)所有元素出棧,進(jìn)入隊(duì)列,直到遇見(jiàn)與之匹配的左括號(hào)
- 最后棧內(nèi)所有元素按順序入列
現(xiàn)在我們開始進(jìn)行變換
最后得到我們的結(jié)果123+4*+
中綴就成功轉(zhuǎn)化成后綴表達(dá)式了
計(jì)算機(jī)是如何理解后綴表達(dá)式的
計(jì)算機(jī)會(huì)將之前放在隊(duì)列里的元素按照先進(jìn)先出(FIFO)的規(guī)則,將元素彈出進(jìn)行判斷
如果元素為數(shù)字,則直接入棧,若元素為運(yùn)算符,則從棧中彈出兩個(gè)數(shù)字進(jìn)行運(yùn)算,再將運(yùn)算結(jié)果放入棧中
當(dāng)隊(duì)列全部元素取出后,最后棧中剩下的唯一一個(gè)元素就是我們要找的結(jié)果了
在GO中的實(shí)現(xiàn)
首先我們需要?jiǎng)?chuàng)造出我們的工具:棧和隊(duì)列
實(shí)現(xiàn)棧
type Stack struct { //定義棧 elements []interface{} //因?yàn)閮?chǔ)存的元素是空接口類型,所以之后要注意類型斷言和類型轉(zhuǎn)化 } func NewStack() *Stack { //返回一個(gè)棧 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 }
實(shí)現(xiàn)隊(duì)列
type Queue struct { //定義隊(duì)列 elements []interface{} } func NewQueue() *Queue { //返回一個(gè)隊(duì)列 return &Queue{} } func (q *Queue) empty() bool { //判斷隊(duì)列是否為空 return len(q.elements) == 0 } func (q *Queue) push(x interface{}) { //將元素壓入隊(duì)列 q.elements = append(q.elements, x) } func (q *Queue) pop() (interface{}, error) { //將最先進(jìn)入的元素記錄并彈出 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)后綴實(shí)現(xiàn)
按照先前的規(guī)則,靈活運(yùn)用判斷語(yǔ)句實(shí)現(xiàn)中綴到后綴表達(dá)式的實(shí)現(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) == "(" { //前一個(gè)是左括號(hào)直接入棧 S.push(string(input[i])) } else { //否則全出 for { t, _ := S.pop() Q.push(t.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" { //直到棧為空或者碰到左括號(hào)為止 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) == "(" { //前一個(gè)是左括號(hào)直接入棧 S.push(string(input[i])) } else { //否則全出 for { t, _ := S.pop() Q.push(t.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" { //直到棧為空或者碰到左括號(hào)為止 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)元素彈出,放入隊(duì)列 t, _ := S.top() if t.(string) == "+" || t.(string) == "-" || t.(string) == "(" { //棧頂為加減號(hào)或左括號(hào),直接入棧 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) == "-" { //直到棧為空或者碰到左括號(hào)為止 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)元素彈出,放入隊(duì)列 t, _ := S.top() if t.(string) == "+" || t.(string) == "-" || t.(string) == "(" { //棧頂為加減號(hào)或左括號(hào),直接入棧 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) == "-" { //直到棧為空或者碰到左括號(hào)為止 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) == "(" { //直到碰到左括號(hào)為止,然后帶走左括號(hào) _, _ = S.pop() break } } default: if '0' <= input[i] && input[i] <= '9' { temp += string(input[i]) } else { return errors.New("valid input") } } } //若還有數(shù)字沒(méi)有入隊(duì)列就入 if temp != "" { Q.push(temp) } //若棧還有運(yùn)算符就出棧 for { if S.empty() { break } t, _ := S.pop() Q.push(t.(string)) } return nil }
計(jì)算過(guò)程的實(shí)現(xiàn)
邏輯十分簡(jiǎn)單,主要注意的是類型間的轉(zhuǎn)化,要從空接口類型斷言為string類型,再將string類型轉(zhuǎn)化為int類型進(jìn)行計(jì)算,使用float類型也可以實(shí)現(xiàn)小數(shù)計(jì)算,可以自己去嘗試
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 實(shí)現(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 實(shí)現(xiàn)隊(duì)列 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) == "(" { //前一個(gè)是左括號(hào)直接入棧 S.push(string(input[i])) } else { //否則全出 for { t, _ := S.pop() Q.push(t.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" { //直到棧為空或者碰到左括號(hào)為止 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) == "(" { //前一個(gè)是左括號(hào)直接入棧 S.push(string(input[i])) } else { //否則全出 for { t, _ := S.pop() Q.push(t.(string)) a, _ := S.top() if S.empty() || a.(string) == "(" { //直到棧為空或者碰到左括號(hào)為止 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)元素彈出,放入隊(duì)列 t, _ := S.top() if t.(string) == "+" || t.(string) == "-" || t.(string) == "(" { //棧頂為加減號(hào)或左括號(hào),直接入棧 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) == "-" { //直到棧為空或者碰到左括號(hào)為止 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)元素彈出,放入隊(duì)列 t, _ := S.top() if t.(string) == "+" || t.(string) == "-" || t.(string) == "(" { //棧頂為加減號(hào)或左括號(hào),直接入棧 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) == "-" { //直到棧為空或者碰到左括號(hào)為止 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) == "(" { //直到碰到左括號(hào)為止,然后帶走左括號(hào) _, _ = S.pop() break } } default: if '0' <= input[i] && input[i] <= '9' { temp += string(input[i]) } else { return errors.New("valid input") } } } //若還有數(shù)字沒(méi)有入隊(duì)列就入 if temp != "" { Q.push(temp) } //若棧還有運(yùn)算符就出棧 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.可輸入加減乘除以及小括號(hào)") fmt.Println("2.只能輸入正整數(shù)") fmt.Println("3.輸入exit退出") for { fmt.Printf("請(qǐng)輸入:") 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) } }
改進(jìn)
上文只實(shí)現(xiàn)了正整數(shù)之間的加減乘除和小括號(hào)的運(yùn)算,圖方便未考慮其他可左右運(yùn)算順序的符號(hào)如:[]中括號(hào) {}大括號(hào) %取余,除此之外還可以嘗試一下實(shí)現(xiàn)輸入負(fù)數(shù)時(shí)處理的方法
以上就是基于Go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器的詳細(xì)內(nèi)容,更多關(guān)于go計(jì)算器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
go?module化?import?調(diào)用本地模塊?tidy的方法
這篇文章主要介紹了go?module化?import?調(diào)用本地模塊?tidy的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09Golang網(wǎng)絡(luò)模型netpoll源碼解析(具體流程)
本文介紹了Golang的網(wǎng)絡(luò)模型netpoll的實(shí)現(xiàn)原理,本文將從為什么需要使用netpoll模型,以及netpoll的具體流程實(shí)現(xiàn)兩個(gè)主要角度來(lái)展開學(xué)習(xí),感興趣的朋友跟隨小編一起看看吧2024-11-11Go項(xiàng)目編寫Makefile規(guī)則文件概述
這篇文章主要為大家介紹了Go項(xiàng)目編寫Makefile文件規(guī)則概述,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04golang?db事務(wù)的統(tǒng)一封裝的實(shí)現(xiàn)
這篇文章主要介紹了golang db事務(wù)的統(tǒng)一封裝的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Go語(yǔ)言開發(fā)k8s之Deployment操作解析
這篇文章主要為大家介紹了Go語(yǔ)言開發(fā)k8s之Deployment操作解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06Go垃圾回收提升內(nèi)存管理效率優(yōu)化最佳實(shí)踐
這篇文章主要為大家介紹了Go垃圾回收提升內(nèi)存管理效率優(yōu)化最佳實(shí)踐,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Go生成base64圖片驗(yàn)證碼實(shí)例(超詳細(xì)工具類)
這段時(shí)間需要使用圖片驗(yàn)證碼庫(kù),下面這篇文章主要給大家介紹了關(guān)于Go生成base64圖片驗(yàn)證碼的相關(guān)資料,文中給出了詳細(xì)的實(shí)例代碼,需要的朋友可以參考下2023-06-06