鱼C论坛

 找回密码
 立即注册
查看: 1850|回复: 27

[已解决]编写一个函数parse,实现计算器功能

[复制链接]
发表于 2020-5-30 13:55:57 | 显示全部楼层 |阅读模式
60鱼币
本帖最后由 Stubborn 于 2020-5-30 16:27 编辑

编写一个函数parse,实现计算器功能,仅支持   + - * / () ,整数。 如果表达式不正确,引发一个异常(不要使用内建函数直接返回答案,例如eval()  )
实例
parse('1+2')
>>>3
parse('1+2*3')
>>>7
parse('1+2*(1+3)')
>>>9
parse('1+9/3')
>>>4


游客,如果您要查看本帖隐藏内容请回复
最佳答案
2020-5-30 13:55:58
本帖最后由 xiaosi4081 于 2020-6-1 18:04 编辑
import re
class suan:
    def calculate(self,n1, n2, operator):

        '''
        :param n1: float
        :param n2: float
        :param operator: + - * /
        :return: float
        '''
        result = 0
        if operator == "+":
            result = n1 + n2
        if operator == "-":
            result = n1 - n2
        if operator == "*":
            result = n1 * n2
        if operator == "/":
            result = n1 / n2
        return result


    # 判断是否是运算符,如果是返回True
    def is_operator(self,e):
        '''
        :param e: str
        :return: bool
        '''
        opers = ['+', '-', '*', '/', '(', ')']
        return True if e in opers else False


    # 将算式处理成列表,解决横杠是负数还是减号的问题
    def formula_format(self,formula):
        # 去掉算式中的空格
        formula = re.sub(' ', '', formula)
        # 以 '横杠数字' 分割, 其中正则表达式:(\-\d+\.?\d*) 括号内:
        # \- 表示匹配横杠开头; \d+ 表示匹配数字1次或多次;\.?表示匹配小数点0次或1次;\d*表示匹配数字1次或多次。
        formula_list = [i for i in re.split('(\-\d+\.?\d*)', formula) if i]

        # 最终的算式列表
        final_formula = []
        for item in formula_list:
            # 第一个是以横杠开头的数字(包括小数)final_formula。即第一个是负数,横杠就不是减号
            if len(final_formula) == 0 and re.search('^\-\d+\.?\d*', item):
                final_formula.append(item)
                continue

            if len(final_formula) > 0:
                # 如果final_formal最后一个元素是运算符['+', '-', '*', '/', '('], 则横杠数字不是负数
                if re.search('[\+\-\*\/\(], final_formula[-1]'):
                    final_formula.append(item)
                    continue
            # 按照运算符分割开
            item_split = [i for i in re.split('([\+\-\*\/\(\)])', item) if i]
            final_formula += item_split
        return final_formula


    def decision(self,tail_op, now_op):
        '''
        :param tail_op: 运算符栈的最后一个运算符
        :param now_op: 从算式列表取出的当前运算符
        :return: 1 代表弹栈运算,0 代表弹运算符栈最后一个元素, -1 表示入栈
        '''
        # 定义4种运算符级别
        rate1 = ['+', '-']
        rate2 = ['*', '/']
        rate3 = ['(']
        rate4 = [')']

        if tail_op in rate1:
            if now_op in rate2 or now_op in rate3:
                # 说明连续两个运算优先级不一样,需要入栈
                return -1
            else:
                return 1

        elif tail_op in rate2:
            if now_op in rate3:
                return -1
            else:
                return 1

        elif tail_op in rate3:
            if now_op in rate4:
                return 0   # ( 遇上 ) 需要弹出 (,丢掉 )
            else:
                return -1  # 只要栈顶元素为(,当前元素不是)都应入栈。
        else:
            return -1


    def final_calc(self,formula_list):
        num_stack = []       # 数字栈
        op_stack = []        # 运算符栈
        for e in formula_list:
            operator = self.is_operator(e)
            if not operator:
                # 压入数字栈
                # 字符串转换为符点数
                num_stack.append(float(e))
            else:
                # 如果是运算符
                while True:
                    # 如果运算符栈等于0无条件入栈
                    if len(op_stack) == 0:
                        op_stack.append(e)
                        break

                    # decision 函数做决策
                    tag = decision(op_stack[-1], e)
                    if tag == -1:
                        # 如果是-1压入运算符栈进入下一次循环
                        op_stack.append(e)
                        break
                    elif tag == 0:
                        # 如果是0弹出运算符栈内最后一个(, 丢掉当前),进入下一次循环
                        op_stack.pop()
                        break
                    elif tag == 1:
                        # 如果是1弹出运算符栈内最后两个元素,弹出数字栈最后两位元素。
                        op = op_stack.pop()
                        num2 = num_stack.pop()
                        num1 = num_stack.pop()
                        # 执行计算
                        # 计算之后压入数字栈
                        num_stack.append(self.calculate(num1, num2, op))
        # 处理大循环结束后 数字栈和运算符栈中可能还有元素 的情况
        while len(op_stack) != 0:
            op = op_stack.pop()
            num2 = num_stack.pop()
            num1 = num_stack.pop()
            num_stack.append(self.calculate(num1, num2, op))

        return num_stack, op_stack

    def pause(self,formula):
        formula_list = self.formula_format(formula)
        result, _ = self.final_calc(formula_list)
        print(result[0])
try:
    p = suan()
    formula = input()
    p.pause(formula)
except:
    print(formula)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 13:55:58 | 显示全部楼层    本楼为最佳答案   
本帖最后由 xiaosi4081 于 2020-6-1 18:04 编辑
import re
class suan:
    def calculate(self,n1, n2, operator):

        '''
        :param n1: float
        :param n2: float
        :param operator: + - * /
        :return: float
        '''
        result = 0
        if operator == "+":
            result = n1 + n2
        if operator == "-":
            result = n1 - n2
        if operator == "*":
            result = n1 * n2
        if operator == "/":
            result = n1 / n2
        return result


    # 判断是否是运算符,如果是返回True
    def is_operator(self,e):
        '''
        :param e: str
        :return: bool
        '''
        opers = ['+', '-', '*', '/', '(', ')']
        return True if e in opers else False


    # 将算式处理成列表,解决横杠是负数还是减号的问题
    def formula_format(self,formula):
        # 去掉算式中的空格
        formula = re.sub(' ', '', formula)
        # 以 '横杠数字' 分割, 其中正则表达式:(\-\d+\.?\d*) 括号内:
        # \- 表示匹配横杠开头; \d+ 表示匹配数字1次或多次;\.?表示匹配小数点0次或1次;\d*表示匹配数字1次或多次。
        formula_list = [i for i in re.split('(\-\d+\.?\d*)', formula) if i]

        # 最终的算式列表
        final_formula = []
        for item in formula_list:
            # 第一个是以横杠开头的数字(包括小数)final_formula。即第一个是负数,横杠就不是减号
            if len(final_formula) == 0 and re.search('^\-\d+\.?\d*', item):
                final_formula.append(item)
                continue

            if len(final_formula) > 0:
                # 如果final_formal最后一个元素是运算符['+', '-', '*', '/', '('], 则横杠数字不是负数
                if re.search('[\+\-\*\/\(], final_formula[-1]'):
                    final_formula.append(item)
                    continue
            # 按照运算符分割开
            item_split = [i for i in re.split('([\+\-\*\/\(\)])', item) if i]
            final_formula += item_split
        return final_formula


    def decision(self,tail_op, now_op):
        '''
        :param tail_op: 运算符栈的最后一个运算符
        :param now_op: 从算式列表取出的当前运算符
        :return: 1 代表弹栈运算,0 代表弹运算符栈最后一个元素, -1 表示入栈
        '''
        # 定义4种运算符级别
        rate1 = ['+', '-']
        rate2 = ['*', '/']
        rate3 = ['(']
        rate4 = [')']

        if tail_op in rate1:
            if now_op in rate2 or now_op in rate3:
                # 说明连续两个运算优先级不一样,需要入栈
                return -1
            else:
                return 1

        elif tail_op in rate2:
            if now_op in rate3:
                return -1
            else:
                return 1

        elif tail_op in rate3:
            if now_op in rate4:
                return 0   # ( 遇上 ) 需要弹出 (,丢掉 )
            else:
                return -1  # 只要栈顶元素为(,当前元素不是)都应入栈。
        else:
            return -1


    def final_calc(self,formula_list):
        num_stack = []       # 数字栈
        op_stack = []        # 运算符栈
        for e in formula_list:
            operator = self.is_operator(e)
            if not operator:
                # 压入数字栈
                # 字符串转换为符点数
                num_stack.append(float(e))
            else:
                # 如果是运算符
                while True:
                    # 如果运算符栈等于0无条件入栈
                    if len(op_stack) == 0:
                        op_stack.append(e)
                        break

                    # decision 函数做决策
                    tag = decision(op_stack[-1], e)
                    if tag == -1:
                        # 如果是-1压入运算符栈进入下一次循环
                        op_stack.append(e)
                        break
                    elif tag == 0:
                        # 如果是0弹出运算符栈内最后一个(, 丢掉当前),进入下一次循环
                        op_stack.pop()
                        break
                    elif tag == 1:
                        # 如果是1弹出运算符栈内最后两个元素,弹出数字栈最后两位元素。
                        op = op_stack.pop()
                        num2 = num_stack.pop()
                        num1 = num_stack.pop()
                        # 执行计算
                        # 计算之后压入数字栈
                        num_stack.append(self.calculate(num1, num2, op))
        # 处理大循环结束后 数字栈和运算符栈中可能还有元素 的情况
        while len(op_stack) != 0:
            op = op_stack.pop()
            num2 = num_stack.pop()
            num1 = num_stack.pop()
            num_stack.append(self.calculate(num1, num2, op))

        return num_stack, op_stack

    def pause(self,formula):
        formula_list = self.formula_format(formula)
        result, _ = self.final_calc(formula_list)
        print(result[0])
try:
    p = suan()
    formula = input()
    p.pause(formula)
except:
    print(formula)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 14:49:17 | 显示全部楼层
给出一个作弊答案:
parse=eval
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 15:31:16 | 显示全部楼层
这不可能实现的,函数的参数必须指定,两个数且不是字符串用逗号隔开才行
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 15:34:44 | 显示全部楼层
这样我能帮你实现
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 15:39:05 | 显示全部楼层
这样我能帮你实现
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 15:42:36 | 显示全部楼层
给你链接给你百度一下百度一下https://www.cnblogs.com/zingp/p/8666214.html
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 15:55:27 | 显示全部楼层
Cool_Breeze 发表于 2020-5-30 15:42
给你链接给你百度一下百度一下https://www.cnblogs.com/zingp/p/8666214.html

我是过运行你给的文件,但是没有输入
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 15:57:23 | 显示全部楼层
liaoyiqin 发表于 2020-5-30 15:55
我是过运行你给的文件,但是没有输入

这个不是我的,大佬写的!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-5-30 16:00:56 | 显示全部楼层
liaoyiqin 发表于 2020-5-30 15:31
这不可能实现的,函数的参数必须指定,两个数且不是字符串用逗号隔开才行

需要我贴代码出来吗,功能是可以实现的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 16:01:37 | 显示全部楼层
Cool_Breeze 发表于 2020-5-30 15:57
这个不是我的,大佬写的!

嗯嗯,这个计算器的操作数是不能随意选择的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 16:09:15 | 显示全部楼层
Stubborn 发表于 2020-5-30 16:00
需要我贴代码出来吗,功能是可以实现的

好的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-5-30 16:29:22 | 显示全部楼层

在隐藏里面了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 16:50:19 | 显示全部楼层
干嘛隐藏
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 16:57:23 | 显示全部楼层
参观大佬的代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 17:02:32 | 显示全部楼层


这样就没意思了呀,晚上我还想写写看呢,虽然很大概率写不出来
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 17:08:24 | 显示全部楼层
liaoyiqin 发表于 2020-5-30 15:31
这不可能实现的,函数的参数必须指定,两个数且不是字符串用逗号隔开才行


不要自己不会就说不可能。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-5-30 17:46:01 | 显示全部楼层
Twilight6 发表于 2020-5-30 17:02
这样就没意思了呀,晚上我还想写写看呢,虽然很大概率写不出来

不要看答案吗,写起来才有意思
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 20:30:44 | 显示全部楼层
答案
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-30 22:06:28 | 显示全部楼层
看看
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-9-28 02:15

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表