评估一个简单的字符串数学表达式

时间:2012-06-23 19:07:21

标签: c++

我刚刚为一位毕业的C ++开发人员测试了下面的问题。它没有太顺利,因为我无法决定完成任务的明确方法。时间限制也没有帮助。我对经验丰富的开发人员如何解决以下问题感兴趣 - 伪代码或示例代码:

Evaluate

Write a function in C or C++ that evaluates the result of a simple expression.
The function should ignore whitespace, but stop at the first non valid character.
Valid tokens are listed in the table below:

0-9 - Only integers are allowed in expressions

() - Nested expressions should be evaluated first.

+, -, *, / - Basic operators are addition, subtraction, multiplication and division.

The expression should be parsed from left to right. It is not necessary to consider operator precedence in your solution (e.g. 1 + 3 * 4 = 16). If there is an error in the expression, the function should return false.

Suggested prototype for function:

Example:

bool evaluate(const char *expression, int &result)
{
...
}

**Input**
1+3
(1 + (12 * 2)

**Result**
4
N/A

**Return code**
true
false (missing bracket)

此外,这是我未能成功完成的第二个C ++。有一年的实习经验和一年的学术经验使用C ++,但我没有准备好进行一些这些测试。我是否有任何建议的资源可以解决这个问题,以获得更多的“测试”体验?

5 个答案:

答案 0 :(得分:2)

这里的问题主要是解析,这可能会在第二年或第三年的编译器课程中介绍。一旦你可以解析表达式来构建表示输入的递归数据结构(称为语法树),评估这样的表达式就非常简单了。递归的正确解析器也可以在不实际构建语法树的情况下评估表达式。

要获得完整的治疗,你需要一本关于编译器的书,比如龙书。 IIRC还有一本书 Programming:Principals and Practice using C ++ 这样的例子。

您还可以等待计算机编程艺术的第十章出版,其中将包括解析。它计划在2020年左右出来。

答案 1 :(得分:1)

这是我最短的尝试。键入大约需要40分钟,你可以在ideone(link)上玩它。

代码非常简单,假设您至少粗略地熟悉基本的recursive descent parsing技术。

#include <iostream>
#include <cctype>
using namespace std;
bool eval_expr(const char **pe, int &lhs, bool inside = false);
// gets the next char after skipping optional whitespace
char skip_ws(const char **pe) {
    while (**pe == ' ') ++(*pe);
    return **pe;
}
// evaluates a parenthesized expression or a number
bool eval_prim(const char **pe, int &res) {
    char c = skip_ws(pe);
    if (c == '(') {
        ++(*pe);
        if (!eval_expr(pe, res, true)) return false;
        ++(*pe);
        return true;
    }
    if (isdigit(c)) {
        res = 0;
        while (isdigit(c)) {
            res = 10*res + c - '0';
            c = *(++(*pe));
        }
        return true;
    }
    return false;
}
// evaluates a chain of + - * / operations
bool eval_expr(const char **pe, int &lhs, bool inside) {
    if (!eval_prim(pe, lhs)) return false;
    char op;
    while ((op = skip_ws(pe)) && (op == '+' || op == '-' || op == '*' || op == '/')) {
        ++(*pe);
        int rhs;
        if (!eval_prim(pe, rhs)) return false;
        switch (op) {
            case '+': lhs += rhs; break;
            case '-': lhs -= rhs; break;
            case '*': lhs *= rhs; break;
            case '/': lhs /= rhs; break;
        }
    }
    return inside ? op == ')' : !op;
}
// wrapper API to hide an extra level of indirection
bool evaluate(const char *e, int &result) {
    return eval_expr(&e, result);
}

答案 2 :(得分:1)

这是一个简单的扫描推送应用(扭曲是大括号)。

  1. 寻找一个数字:
    • 如果你看到一个数字推入堆栈
    • 如果您看到'('将其推入堆栈并转到1
    • 否则出错。
  2. 寻找一个操作:
    • 如果你看到op将其推入堆栈
    • 否则出错
  3. 寻找一个数字:
    • 如果你看到一个数字推入堆栈
    • 如果你看到'('推入堆栈和转到1
    • 否则出错
  4. 从堆栈中弹出最后三个项目(应该是编号操作编号)
    • 执行操作并将结果推送到堆栈。
  5. 现在复杂的位:
    • 如果下面的字符是“PopCode”,请查看下一个字符是否为')'
  6. 如果没有更多输入转到7
    • Otherewise 转到2
  7. 如果堆叠中只有一个项目,则表示您拥有结果。
    • 否则出错。
  8. Pop代码

    1. 从堆栈中弹出最后两个值。应该是'(数字'
      • 如果不是则错误
    2. 丢掉'('
    3. 如果堆栈顶部是op推送值 goto 4(上图)
    4. 否则将值推入堆栈转到5(上方)
    5. 完成后,堆叠中应该有一个数字。

      示例:

      1+3
      Rule 1: push 1             stack = '1'
      Rule 2: push +             stack = '1 +'
      Rule 3: push 3             stack = '1 + 3'
      Rule 4: pop and do:        stack = '4'
      Rule 5: Nothing            stack = '4'
      Rule 6: goto 7             stack = '4'
      Rule 7:                    stack = '4'
      
      (1 + (12 * 2)
      Rule 1: push ( goto 1      stack = '('
      Rule 1: push 1             stack = '( 1'
      Rule 2: push +             stack = '( 1 +'
      Rule 3: push ( goto 1      stack = '( 1 + ('
      Rule 1: push 12            stack = '( 1 + ( 12'
      Rule 2: push *             stack = '( 1 + ( 12 *'
      Rule 3: push 2             stack = '( 1 + ( 12 * 2'
      Rule 4: Pop and do:        stack = '( 1 + ( 24'
      Rule 5: Do 'PopCode'       stack = '( 1 + ( 24'
      Pop  1: Pop 2              stack = '( 1 +'
      Pop  2: Holding 24         stack = '( 1 +'
      Pop  3: push 24 goto 4     stack = '( 1 + 24'
      Rule 4: Pop and do         stack = '( 25'
      Rule 5: Nothing            stack = '( 25'
      Rule 6: goto 7             stacj = '( 25'
      Rule 7: More than 1 item error
      
      Re-Doing with correct formula
      (1 + (12 * 2))
      Rule 1: push ( goto 1      stack = '('
      Rule 1: push 1             stack = '( 1'
      Rule 2: push +             stack = '( 1 +'
      Rule 3: push ( goto 1      stack = '( 1 + ('
      Rule 1: push 12            stack = '( 1 + ( 12'
      Rule 2: push *             stack = '( 1 + ( 12 *'
      Rule 3: push 2             stack = '( 1 + ( 12 * 2'
      Rule 4: Pop and do:        stack = '( 1 + ( 24'
      Rule 5: Do 'PopCode'       stack = '( 1 + ( 24'
      Pop  1: Pop 2              stack = '( 1 +'
      Pop  2: Holding 24         stack = '( 1 +'
      Pop  3: push 24 goto 4     stack = '( 1 + 24'
      Rule 4: Pop and do         stack = '( 25'
      Rule 5: Do 'PopCode'       stack = '( 25'
      Pop  1: Pop 2              stack = ''
      Pop  2: holding 25         stack = ''
      Pop  3: Nothing.           stack = ''
      Pop  4: push 25 goto 5     stack = '25'
      Rule 5: Nothing            stack = '25'
      Rule 6: goto 7             stack = '25'
      Rule 7: Result = 25
      

答案 3 :(得分:1)

从简单的语法开始:

expr: n-expr {o-expr} | p-expr {o-expr}
n-expr: [0-9]n-expr
p-expr: ( expr )
o-expr: op expr
op: + | - | * | /

这可能是这个问题的最大障碍。您希望能够编写一个简单的自上而下的递归下降解析器,因此您的语法需要以允许其发生的方式编写。

然后,从那里实现相当简单:

bool expr (const char *&s, int &result, int eos = 0) {
    while (isspace(*s)) ++s;
    if (*s == eos) return false;
    if (isdigit(*s)) {
        if (!n_expr(s, result)) return false;
    } else if (*s == '(') {
        if (!p_expr(s, result)) return false;
    } else return false;
    while (isspace(*s)) ++s;
    if (*s == eos) return true;
    return o_expr(s, result, eos);
}

bool n_expr (const char *&s, int &result) {
    int n = 0;
    while (isdigit(*s)) n = 10 * n + (*s++ - '0');
    result = n;
    return true;
}

bool p_expr (const char *&s, int &result) {
    if (expr(++s, result, ')')) {
        ++s;
        return true;
    }
    return false;
}

bool o_expr (const char *&s, int &result, int eos) {
    int oresult = 0;
    const char *op = strchr("+-*/", *s);
    if (op == 0) return false;
    if (!expr(++s, oresult, eos)) return false;
    switch (*op) {
    case '+': result += oresult; break;
    case '-': result -= oresult; break;
    case '*': result *= oresult; break;
    case '/': result /= oresult; break;
    default: return false;
    }
    return true;
}

答案 4 :(得分:0)

解决(不一定)简单数学表达式的最简单方法是使用Shunting Yard algorithm将其转换为Reverse Polish Notation,使用堆栈解析几乎是微不足道的。当然,对于作业或面试来说这可能是不可行的(可能除非有SY算法参考可用)。