【问题标题】:"+" Quantifier not doing its job?“+”量词没有发挥作用?
【发布时间】:2014-06-08 15:06:14
【问题描述】:

我是一名新手程序员,用 JavaScript 为学校项目制作了一个简单的计算器,我没有使用 eval() 来计算字符串,而是创建了自己的函数 calculate(exp)

基本上,我的程序使用运算顺序(PEMDAS,或括号、指数、乘法/除法、加法/减法)来评估字符串表达式。我的正则表达式模式之一是这样的(“mdi”表示乘法/除法):

mdi = /(-?\d+(\.\d+)?)([\*\/])(-?\d+(\.\d+)?)/g;    // line 36 on JSFiddle

这是做什么的:

  • -?\d+ 找到一个整数

  • (\.\d+)? 匹配小数点(如果有的话)

  • [\*\/] 匹配使用的运算符(*/ 用于乘法或除法)

  • /g 匹配字符串表达式中的每个出现。

我用下面的代码循环这个正则表达式的匹配:

while((res = mdi.exec(exp)) !== null) {    // line 69 on JSFiddle
    exp = exp.replace(mdi,
        function(match,$1,$3,$4,$5) {
            if($4 == "*")
                return parseFloat($1) * parseFloat($5);
            else
                return parseFloat($1) / parseFloat($5);
        });
    exp = exp.replace(doN,"");    // this gets rid of double negatives
}

但是,这并不总是有效。它仅适用于绝对值小于 10 的数字。我无法对 24 和 -5232000321 等数字执行任何操作,即使正则表达式应与 + 量词匹配。它适用于小数字,但当数字大于 10 时会崩溃并耗尽我的大部分 CPU。

例如输入5*.5表达式时,输出2.5,但输入75*.5并回车时,程序停止。

我不太确定这里发生了什么,因为由于某种原因我无法找到错误的根源 - 即使我的代码中到处都有 console.log() 进行调试,但我认为它没有任何显示这个正则表达式有问题。发生了什么?

完整的代码(到目前为止)在JSFiddle.net,但请注意它可能会崩溃。如果您有任何其他建议,也请告诉我。

感谢您的帮助。

【问题讨论】:

  • 尝试删除 g 标志
  • 另外,这是编写计算器解析器的一种非常复杂的方法。如果这就是你正在做的事情。
  • @BenjaminGruenbaum 再说一次,我不是专业人士,我不确定该怎么做。如果您有任何建议,请告诉我。
  • 听起来你可能有一个catastrophic backtracking 的案例。考虑到字符串 '123*456' 还包括字符串 '23*456'、'3*456'、'123*45' 和许多其他组合。您需要检测“令牌”(例如多位数字)的开始和结束位置。
  • 子模式\d+(\.\d+)? 无法匹配没有前导数字的浮点数,例如.5

标签: javascript regex match


【解决方案1】:

问题是

bzp = /^.\d/;
while((res = bzp.exec(result)) !== null) {
  result = result.replace(bzp,
    function($match) {
      console.log($match + " -> 0 + " + $match);
      return "0" + $match;
    });
}

它不断在前面添加零,没有限制。

删除该代码效果很好。

我还清理了您的代码,声明了变量,并使其更易于维护:Demo

【讨论】:

    【解决方案2】:

    如果您有任何其他建议,也请告诉我。

    正如 cmets 中所指出的,通过迭代应用正则表达式来解析您的输入是非常临时的。更好的方法是为您的输入语言实际构造一个grammar 并基于它进行解析。这是一个与您的输入语言基本匹配的示例语法:

    expr ::= term ( additiveOperator term )*
    term ::= factor ( multiplicativeOperator factor )*
    expr ::= number | '(' expr ')'
    
    additiveOperator ::= '+' | '-'
    multiplicativeOperator ::= '*' | '/'
    

    这里的语法与正则表达式非常相似,其中括号表示组,* 表示零次或多次重复,|表示替代品。用单引号括起来的符号是文字,而其他一切都是象征性的。请注意,此语法不处理一元运算符(根据您的帖子,听起来您假设负数只有一个负号,可以由 number 解析器解析)。

    有几个用于 JavaScript 的解析器生成器库,但我更喜欢组合器样式的解析器,其中解析器是在运行时按功能构建的,而不必运行单独的工具来为您的解析器生成代码。 Parsimmon 是一个不错的 JavaScript 组合解析器,而且 API 很容易理解。

    解析器通常会返回某种与解析语法相对应的树数据结构(即abstract syntax tree)。然后遍历这个数据结构来计算算术表达式的值。

    我创建了a fiddle demonstrating parsing and evaluating of arithmetic expressions。我没有将任何这些集成到您现有的计算器界面中,但如果您能理解如何使用解析器

    【讨论】:

      【解决方案3】:

      由于可用的排列和组合的数量,数学表达式不使用正则表达式进行解析和计算。到目前为止,更快的方法是 POST FIX 表示法,因为其他表示法没有这个快。正如他们在维基百科上提到的:

      在逆波兰符号与代数的比较测试中 符号,已发现反向波兰语会导致更快 计算,有两个原因。因为逆波兰计算器可以 不需要为表达式加上括号,需要更少的操作 输入以执行典型计算。此外,用户 逆波兰计算器比其他类型的计算器犯的错误更少 计算器。后来的研究表明,增加的速度 从反向波兰表示法可能归因于较小的数字 输入此符号所需的击键次数,而不是较小的 对其用户的认知负担。然而,轶事证据表明 反向波兰符号比用户更难学习 代数符号。

      全文:Reverse Polish Notation

      您还可以在这里看到比正则表达式更好的其他表示法。

      Calculator Input Methods

      因此,我建议您将算法更改为更有效的算法,我个人更喜欢 POST FIX。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-03-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多