此计算器解析错误的解决方法

Workaround for this calculator parsing error

上下文:

我在自己制作的计算器中输入了一个表达式3.24 * 10^10 + 1。 我的计算器解决这个问题的方法是 - 它首先查找模式 number_a^number_b,使用 Double.parseDouble() 方法将 2 个数字解析为双精度数,然后执行 Math.pow(number_a, number_b) 并用结果替换表达式。

计算器然后类似地查找模式 number_a * number_b 并解析它。至此我们的表达式变成了3.24E10 + 1。现在是棘手的部分。当我编写这个计算器时,我考虑到计算器应该找到模式 number_a + number_b 并解析它。我的计算器确实做到了这一点,并且 returns 结果出乎意料但合理 - 3.24E11.0.

我正在寻找解决方法,使我的计算器足够智能以处理此类表达式。

重要信息 - 正则表达式示例 = ([\d\.]+)\*([\d\.]+)

代码示例 -

// here 'expression' is a StringBuilder type
// only a (modified) snippet of actual code.

Matcher m = Pattern.compile ("([\d\.]+)\^([\d\.]+)")
                           .matcher (expression.toString());
while (m.find()) {
     Double d1 = Double.parseDouble(m.group(1));
     Double d2 = Double.parseDouble(m.group(2));
     Double d3 = Math.pow(d1, d2);
     expression.replace(m.start(), m.end(), Double.toString(d3));
     m.reset(expression);
}

PS :根据我提出问题的方式,很多人似乎认为我的计算器是一次失败的尝试,因为正则表达式不会带我太远的。当然,我同意这是真的,并且可能存在更好的算法。我只想说清楚:-

1) 正则表达式仅用于解析直接形式的表达式。我不会对所有内容都使用正则表达式。嵌套括号使用递归解决。正则表达式只在最后一步发挥作用,当所有的处理工作都已经完成,剩下的只是简单的计算。

2) 我的计算器工作正常。它可以并且确实优雅地解决了嵌套表达式。证明 - 2^3*2/4+1 --> 5.0sin(cos(1.57) + tan(cos(1.57)) + 1.57) --> 0.9999996829318346((3(2log(10))+1)+1)exp(0) --> 8.0

3) 不使用太多'crutches'。如果您认为我已经编写了数千行代码来获得所需的功能。第 200 行,仅此而已。而且我无意放弃我的申请(即将完成)。

if you could provide me a justification for why the regex is not a good fit

  1. 真正的正则表达式无法正确解析嵌套/平衡括号。 (好的,可以使用高级正则表达式功能来完成,但结果非常难以理解1。)

  2. 真正的正则表达式将难以分析具有不同优先级的运算符的表达式。特别是带括号。 (我不确定是不是不可能,但肯定是很难的。)

  3. 一旦您使用了正则表达式来匹配表达式,您就会遇到将匹配的 "groups" 整理成允许您(正确) 计算表达式。

  4. 如果输入在语法上无效,则正则表达式无法产生任何解释。

  5. 复杂的正则表达式通常在病态上非常昂贵......尤其是对于不正确的大输入字符串。

what exactly do the other algorithms have that make them superior.

一个正确编写或生成的词法分析器+解析器将有 none 以上问题。您可以即时计算表达式,也可以将其变成可以重复计算的解析树;例如具有不同的变量值。

调车场算法(虽然应用更有限)也有 none 以上问题。


这是关于为工作选择正确的工具。还要认识到正则表达式并不是适合所有工作的工具。


1 - 如果你想探索使用正则表达式解析嵌套结构的兔子沃伦,here 是一个入口。

根据您的评论,通过将正则表达式更改为:

([\d\.]+)\*([\d\.]+)

此作品:

(\d+(\.\d+)?(e\d+)?)\^(\d+(\.\d+)?(e\d+)?)

解释一下我所做的更改:之前,您可以按以下格式输入数字:

  1. 1
  2. .5
  3. .......
  4. .3.76
  5. 等等

为了克服这个问题:我添加了一个可选的小数位 ((\.\d+)?),它允许整数,但也允许小数。

还可以通过在两边添加一个可选的科学记数法((e\d+)?),允许写入数字:

  1. 作为整数 (2 ^ 5)
  2. 小数形式 (2.3 ^ 5.7)
  3. 和科学一样 (2.345e2 ^ 5e10)

您当然可以混合所有变体。

但请记住您问题下方的评论。正则表达式可能对小部分有用,但它会变得非常笨拙、缓慢和混乱,方程式越大。

此外,如果你想支持负数,你可以在底数和指数前添加可选的连字符:

(-?\d+(\.\d+)?(e-?\d+)?)\^(-?\d+(\.\d+)?(e-?\d+)?)