基于Go语言实现简单的计算器

 更新时间:2023年10月30日 09:08:15   作者:Keiichi  
这篇文章主要为大家详细介绍了如何基于Go语言实现简单的计算器,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以跟随小编一起了解一下

计算器实现原理

我们平时见到的算式都是这种类型 1+(2+3)*4,这种类型的表达式也被称为中缀表达式,我们很容易理解它的运算顺序。但是计算机却无法理解这个式子

因此我们需要将其转化为便于计算机理解的式子,转化为后缀表达式或者前缀表达式(其实都差不多)。在这里我们以后缀表达式为例子。

中缀表达式如何转化为后缀表达式

1+(2+3)*4的后缀表达式为123+4*+,前者转换成后者的过程需要利用到栈和队列这两个数据结构,不太清楚可以看看:栈和队列详解

首先我们需要一个栈和队列

然后从左到右依次根据一定规则判断是否入栈

入栈规则如下:

  • 数字直接入队列
  • 若是运算符,则判断其与栈顶符号的优先级,优先级低于或等于栈顶符号,栈内元素不断出栈,进入队列,直到栈空或者碰见左括号为止
  • 若是左括号则直接入栈
  • 若是右括号则栈内所有元素出栈,进入队列,直到遇见与之匹配的左括号
  • 最后栈内所有元素按顺序入列

现在我们开始进行变换

最后得到我们的结果123+4*+中缀就成功转化成后缀表达式了

计算机是如何理解后缀表达式的

计算机会将之前放在队列里的元素按照先进先出(FIFO)的规则,将元素弹出进行判断
如果元素为数字,则直接入栈,若元素为运算符,则从栈中弹出两个数字进行运算,再将运算结果放入栈中
当队列全部元素取出后,最后栈中剩下的唯一一个元素就是我们要找的结果了

在GO中的实现

首先我们需要创造出我们的工具:栈和队列

实现栈

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
}

实现队列

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 Transform(S *Stack, Q *Queue, input string) error {
   temp := ""
   for i := 0; i < len(input); i++ {
      switch string(input[i]) {
      case "+":
         //前面有数字堆着就先让数字入列
         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 "-":
         //前面有数字堆着就先让数字入列
         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 "*":
         //前面有数字堆着就先让数字入列
         if temp != "" {
            Q.push(temp)
            temp = ""
         }

         if S.empty() { //如果栈为空直接入栈
            S.push(string(input[i]))
         } else { //反之,将栈内元素弹出,放入队列
            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 "/":
         //前面有数字堆着就先让数字入列
         if temp != "" {
            Q.push(temp)
            temp = ""
         }

         if S.empty() { //如果栈为空直接入栈
            S.push(string(input[i]))
         } else { //反之,将栈内元素弹出,放入队列
            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 ")":
         //前面有数字堆着就先让数字入列
         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")
         }
      }
   }
   //若还有数字没有入队列就入
   if temp != "" {
      Q.push(temp)
   }
   //若栈还有运算符就出栈
   for {
      if S.empty() {
         break
      }
      t, _ := S.pop()
      Q.push(t.(string))
   }
   return nil
}

计算过程的实现

逻辑十分简单,主要注意的是类型间的转化,要从空接口类型断言为string类型,再将string类型转化为int类型进行计算,使用float类型也可以实现小数计算,可以自己去尝试

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 实现栈
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 实现队列
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 "+":
         //前面有数字堆着就先让数字入列
         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 "-":
         //前面有数字堆着就先让数字入列
         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 "*":
         //前面有数字堆着就先让数字入列
         if temp != "" {
            Q.push(temp)
            temp = ""
         }

         if S.empty() { //如果栈为空直接入栈
            S.push(string(input[i]))
         } else { //反之,将栈内元素弹出,放入队列
            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 "/":
         //前面有数字堆着就先让数字入列
         if temp != "" {
            Q.push(temp)
            temp = ""
         }

         if S.empty() { //如果栈为空直接入栈
            S.push(string(input[i]))
         } else { //反之,将栈内元素弹出,放入队列
            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 ")":
         //前面有数字堆着就先让数字入列
         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")
         }
      }
   }
   //若还有数字没有入队列就入
   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("输入规则:")
   fmt.Println("1.可输入加减乘除以及小括号")
   fmt.Println("2.只能输入正整数")
   fmt.Println("3.输入exit退出")
   for {

      fmt.Printf("请输入:")
      scanner := bufio.NewScanner(os.Stdin)
      scanner.Scan() // 读取输入内容,直到遇到换行符(包括空格)
      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("结果为: ", ret)
   }
}

改进

上文只实现了正整数之间的加减乘除和小括号的运算,图方便未考虑其他可左右运算顺序的符号如:[]中括号 {}大括号 %取余,除此之外还可以尝试一下实现输入负数时处理的方法

以上就是基于Go语言实现简单的计算器的详细内容,更多关于go计算器的资料请关注脚本之家其它相关文章!

相关文章

  • 试了下Golang实现try catch的方法

    试了下Golang实现try catch的方法

    虽然在使用Golang的时候发现没有try catch这种错误处理机制但是想一想golang作为一门优雅的语言,似乎也是情理之中。那么够怎么捕获异常呢,本文就来介绍一下
    2021-07-07
  • 解决golang http重定向失效的问题

    解决golang http重定向失效的问题

    这篇文章主要介绍了解决golang http重定向失效的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 基于Go语言实现应用IP防火墙

    基于Go语言实现应用IP防火墙

    在公司里面经常会听到某应用有安全漏洞问题,没有做安全加固,IP防火墙就是一个典型的安全加固解决方案,下面我们就来学习一下如何使用go语言实现IP防火墙吧
    2023-11-11
  • golang协程关闭踩坑实战记录

    golang协程关闭踩坑实战记录

    协程(coroutine)是Go语言中的轻量级线程实现,下面这篇文章主要给大家介绍了关于golang协程关闭踩坑的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-03-03
  • GO语言中接口和接口型函数的具体使用

    GO语言中接口和接口型函数的具体使用

    本文主要介绍了GO语言中接口和接口型函数的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • Go 中闭包的底层原理

    Go 中闭包的底层原理

    这篇文章主要介绍了Go 中闭包的底层原理,闭包的基本原理是一种现象,一个函数内引用了外部的局部变量的现象,带着些许的了解和小编一起进入文章正题学习
    2021-10-10
  • Golang中的泛型你真的了解吗

    Golang中的泛型你真的了解吗

    Golang 在 1.18 版本更新后引入了泛型,这是一个重要的更新,Gopher 万众瞩目,为 Golang 带来了更多的灵活性和可重用性,今天,我们将深入探讨泛型的概念、为什么需要泛型、泛型的语法,并探讨如何在实践中使用它
    2023-05-05
  • golang进程在docker中OOM后hang住问题解析

    golang进程在docker中OOM后hang住问题解析

    这篇文章主要介绍了golang进程在docker中OOM后hang住问题解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • 深入理解go reflect反射慢的原因

    深入理解go reflect反射慢的原因

    我们选择 go 语言的一个重要原因是,它有非常高的性能。但是它反射的性能却一直为人所诟病,本篇文章就来看看 go 反射的性能问题,感兴趣的可以了解一下
    2023-01-01
  • golang频率限制 rate详解

    golang频率限制 rate详解

    这篇文章主要介绍了golang频率限制 rate详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12

最新评论