0

我正在尝试创建一个计算器程序,用户可以在其中输入方程式并获得答案。我不想要完整的代码,我只需要特定部分的帮助。

我尝试采用的方法是让用户将方程输入为字符串 ( raw_input),然后我尝试将输入的数字转换为整数。之后,我需要知道如何让操作数根据用户使用的操作数以及它在方程式中的位置来执行我希望他们执行的操作。

我可以使用哪些方法来完成此任务?

这基本上是我现在所拥有的:

    equation_number = raw_input("\nEnter your equation now: ")
    [int(d) for d in equation_number if d.isdigit()]

这些行仅用于收集输入并尝试将数字转换为整数。不幸的是,它似乎并没有很好地工作,而且 .isdigit 无论如何都只适用于正数。

Edit-aong152 提到了我研究过的递归解析,它似乎有理想的结果:

http://blog.erezsh.com/how-to-write-a-calculator-in-70-python-lines-by-writing-a-recursive-descent-parser/

但是,我不明白这篇文章的作者使用的代码,谁能让我熟悉递归解析的基础知识?

4

4 回答 4

1

您尝试制作的程序类型可能比您想象的要复杂

第一步是将字符串分成每个参数。

假设用户输入:

1+2.0+3+4

在您甚至可以转换为整数之前,您需要将字符串拆分为其组件:

  • 1
  • +
  • 2.0
  • +
  • 3
  • +
  • 4

这将需要一个递归解析器,这(因为您是 python 新手)可能有点障碍。

假设您现在将每个部分分别作为字符串,

float("2.0") = 2.0
int(2.0) = 2

这是一个辅助函数

def num (s):
    try:
        return int(s)
    except exceptions.ValueError:
        return int(float(s))
于 2013-05-09T00:41:29.963 回答
0

而不是raw_input仅仅使用input因为raw_input返回一个字符串并input返回整数

这是一个非常简单的计算器:

def calculate():
    x = input("Equation: ")
    print x
while True:
    calculate()

该函数接受input并打印它然后while循环再次执行它

我不确定这是否是您想要的,但是您可以这样做,而且您应该设法结束循环

于 2013-05-09T00:31:18.597 回答
0

使用后,raw_input()您可以使用eval()结果来计算此字符串的值。 eval()计算任何有效的 Python 表达式并返回结果。

但我认为这不是你喜欢的。你可能想自己做更多的事情。

因此,我认为您应该查看re使用正则表达式将输入拆分为标记(例如数字和运算符)的模块。在此之后,您应该编写一个解析器,它将令牌流作为输入。您应该决定这个解析器是否应该只返回计算值(例如一个数字)或者可能是一个抽象语法树,即以面向对象(而不是面向字符)方式表示表达式的数据结构。然后可以评估这样的 Absy 以获得最终结果。

于 2013-05-09T00:36:58.227 回答
0

你熟悉正则表达式吗?如果没有,最好先了解它们。它们是解析的弱的、非递归的表亲。不要深入,只需了解构建块 - A 然后 B,A 很多次,A 或 B。

您找到的博客文章很难,因为它手动实现了解析。它使用递归下降,这是手动编写解析器并保持理智的唯一方法,但它仍然很棘手。

人们大部分时间所做的只是编写高级语法并使用库(或代码生成器)来完成解析的艰苦工作。事实上,他有一个较早的帖子,他使用了一个库: http ://blog.erezsh.com/how-to-write-a-calculator-in-50-python-lines-without-eval/ 至少开头应该是很容易。需要注意的事项:

  • 优先级是如何从语法结构中产生的——addmuls 组成,反之亦然。

  • 他为括号添加规则的那一刻:

    atom: neg | number | '(' add ')';
    

    这就是它真正变得递归的地方!

  • 6-2-1应该解析为 (6-2)-1,而不是 6-(2-1)。他不讨论,但如果你仔细看,它也来自语法的结构。不要在这上面浪费时间;只是知道这称为关联性以供将来参考。

  • 解析的结果是一棵树。然后,您可以以自下而上的方式计算其值。在“计算!” 章他这样做了,但是以一种神奇的方式。别担心。


自己做一个计算器,我建议你尽量去掉这个问题。

  1. 识别数字在哪里结束等有点混乱。它可以是语法的一部分,也可以由称为lexertokenizer的单独通道完成。
    我建议你跳过它——要求用户在所有运算符和括号周围输入空格。或者只是假设您已经获得了一个表单列表[2.0, "*", "(", 3.0, "+", -1.0, ")"]

  2. 从一个仅处理 3 元素表达式的简单解析器(令牌)函数开始 - [number, op, number]。
    返回一个数字,即计算的结果。(我之前说过解析器输出一个稍后处理的树。不用担心,返回一个数字更简单。)

  3. 编写一个需要数字或括号的函数——在后一种情况下,它调用 parser()。

    >>> number_or_expr([1.0, "rest..."])
    (1.0, ["rest..."])
    >>> number_or_expr(["(", 2.0, "+", 2.0, ")", "rest..."])
    (4.0, ["rest..."])
    

    请注意,我现在返回第二个值 - 输入的剩余部分。更改 parser() 以也使用此约定。

  4. 现在重写 parser() 来调用 number_or_expr() 而不是直接假设 tokens[0] 和 tokens[2] 是数字。
    中提琴!你现在有一个可以计算任何东西的(相互的)递归计算器——它只需要以冗长的风格编写,并在所有内容周围加上括号。

现在停下来欣赏你的代码,至少花一天时间 :-) 它仍然很简单,但具有解析的基本递归性质。并且代码结构反映了语法 1:1(这是递归下降的好特性。你不想知道其他算法的外观)。

从这里可以进行许多改进——支持 2+2+2、允许 (1)、优先级...——但有两种方法可以解决:

  • 逐步改进您的代码。你将不得不重构很多。
  • 停止努力并使用解析库,例如pyparsing。这将使您能够更快地尝试语法更改。
于 2013-05-09T09:36:44.517 回答