此计算器解析错误的解决方法
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.0
、sin(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。)
真正的正则表达式将难以分析具有不同优先级的运算符的表达式。特别是带括号。 (我不确定是不是不可能,但肯定是很难的。)
一旦您使用了正则表达式来匹配表达式,您就会遇到将匹配的 "groups" 整理成允许您(正确) 计算表达式。
如果输入在语法上无效,则正则表达式无法产生任何解释。
复杂的正则表达式通常在病态上非常昂贵......尤其是对于不正确的大输入字符串。
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
.5
.......
.3.76
- 等等
为了克服这个问题:我添加了一个可选的小数位 ((\.\d+)?
),它允许整数,但也允许小数。
还可以通过在两边添加一个可选的科学记数法((e\d+)?
),允许写入数字:
- 作为整数 (
2 ^ 5
)
- 小数形式 (
2.3 ^ 5.7
)
- 和科学一样 (
2.345e2 ^ 5e10
)
您当然可以混合所有变体。
但请记住您问题下方的评论。正则表达式可能对小部分有用,但它会变得非常笨拙、缓慢和混乱,方程式越大。
此外,如果你想支持负数,你可以在底数和指数前添加可选的连字符:
(-?\d+(\.\d+)?(e-?\d+)?)\^(-?\d+(\.\d+)?(e-?\d+)?)
上下文:
我在自己制作的计算器中输入了一个表达式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.0
、sin(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。)
真正的正则表达式将难以分析具有不同优先级的运算符的表达式。特别是带括号。 (我不确定是不是不可能,但肯定是很难的。)
一旦您使用了正则表达式来匹配表达式,您就会遇到将匹配的 "groups" 整理成允许您(正确) 计算表达式。
如果输入在语法上无效,则正则表达式无法产生任何解释。
复杂的正则表达式通常在病态上非常昂贵......尤其是对于不正确的大输入字符串。
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
.5
.......
.3.76
- 等等
为了克服这个问题:我添加了一个可选的小数位 ((\.\d+)?
),它允许整数,但也允许小数。
还可以通过在两边添加一个可选的科学记数法((e\d+)?
),允许写入数字:
- 作为整数 (
2 ^ 5
) - 小数形式 (
2.3 ^ 5.7
) - 和科学一样 (
2.345e2 ^ 5e10
)
您当然可以混合所有变体。
但请记住您问题下方的评论。正则表达式可能对小部分有用,但它会变得非常笨拙、缓慢和混乱,方程式越大。
此外,如果你想支持负数,你可以在底数和指数前添加可选的连字符:
(-?\d+(\.\d+)?(e-?\d+)?)\^(-?\d+(\.\d+)?(e-?\d+)?)