我刚刚为一位毕业的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 ++,但我没有准备好进行一些这些测试。我是否有任何建议的资源可以解决这个问题,以获得更多的“测试”体验?
答案 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)
这是一个简单的扫描推送应用(扭曲是大括号)。
Pop代码
完成后,堆叠中应该有一个数字。
示例:
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算法参考可用)。