题目
给定一个类似 Lisp 语句的表达式 expression,求出其计算结果。
表达式语法如下所示:
表达式可以为整数,let 语法,add 语法,mult 语法。表达式的结果总是一个整数。
(整数可以是正整数、负整数、0)
let 语法表示为 (let v1 e1 v2 e2 ... vn en expr), 其中 let语法总是以字符串 "let"来表示,接下来会跟随一个或多个交替变量或表达式,也就是说,第一个变量 v1被分配为表达式 e1 的值,第二个变量 v2 被分配为表达式 e2 的值,以此类推;最终 let 语法的值为 expr表达式的值。
add语法表示为 (add e1 e2),其中 add 语法总是以字符串 "add"来表示,该语法总是有两个表达式e1、e2, 该语法的最终结果是 e1 表达式的值与 e2 表达式的值之和。
mult语法表示为 (mult e1 e2) ,其中 mult 语法总是以字符串"mult"表示, 该语法总是有两个表达式 e1、e2,该语法的最终结果是 e1 表达式的值与 e2 表达式的值之积。
在该题目中,变量的命名以小写字符开始,之后跟随0个或多个小写字符或数字。为了方便,"add","let","mult"会被定义为"关键字",不会在表达式的变量命名中出现。
最后,要说一下范围的概念。在做计算时,需要注意优先级,在最内层(根据括号)的表达式的值应该先计算,然后依次计算外层的表达式。我们将保证每一个测试的表达式都是合法的。有关范围的更多详细信息,请参阅示例。
示例:
输入: (add 1 2)
输出: 3
输入: (mult 3 (add 2 3))
输出: 15
输入: (let x 2 (mult x 5))
输出: 10
输入: (let x 2 (mult x (let x 3 y 4 (add x y))))
输出: 14
解释:
表达式 (add x y), 在获取 x 值时, 我们应当由最内层依次向外计算, 首先遇到了 x=3, 所以此处的 x 值是 3.
输入: (let x 3 x 2 x)
输出: 2
解释: let 语句中的赋值运算按顺序处理即可
输入: (let x 1 y 2 x (add x y) (add x y))
输出: 5
解释:
第一个 (add x y) 计算结果是 3,并且将此值赋给了 x 。
第二个 (add x y) 计算结果就是 3+2 = 5 。
输入: (let x 2 (add (let x 3 (let x 4 x)) x))
输出: 6
解释:
(let x 4 x) 中的 x 的作用范围仅在()之内。所以最终做加法操作时,x 的值是 2 。
输入: (let a1 3 b2 (add a1 1) b2)
输出: 4
解释:
变量命名时可以在第一个小写字母后跟随数字.
注意:
我们给定的 expression 表达式都是格式化后的:表达式前后没有多余的空格,表达式的不同部分(关键字、变量、表达式)之间仅使用一个空格分割,并且在相邻括号之间也没有空格。我们给定的表达式均为合法的且最终结果为整数。
我们给定的表达式长度最多为 2000 (表达式也不会为空,因为那不是一个合法的表达式)。
最终的结果和中间的计算结果都将是一个 32 位整数。
解题思路
建议在做这道题之前先看一下leetcode上432 Mini-Parser那道题。
这道题应该首先把词法单位分解出来,然后递归解析词法单位。很容易掉进去的一个坑是用空格解析词法单位。但是实际上只有没有左括号 '(' 存在的时候这个解析方式才是对的,比如 '(add 1 2)' 这种算一个词法单位(然后递归解析即可),但如果按照空格会拆出3个。
第二个需要注意的地方是变量作用域的问题。变量值是可以从上层往下层传递的,但是不能反过来。所以在返回函数的时候(返回上层),必须把这一层的变量变化清空。
其中一个case就是 "(let x 2 (add (let x 3 (let x 4 x)) x )", 可以感受一下x的作用域的区别。(这个case的正确值是6,没有考虑变量就会变成8)。代码可以有很多assumption的方面是在于题目给出的“表达式一定为正确的且没有多余空格”的前提。
代码
class Solution {
public:
map<string, int> var;
int inf = 1 << 30;
void getTokens(string e, vector<string>& res){
int cnt = 0, start = 0;
if(e[0] == '('){ start = 1; }
for(int i = start; i < e.size(); i++){
if(cnt == 0 && (e[i] == ' ' || e[i] == ')')){
res.emplace_back(e.substr(start, i - start));
start = i + 1;
}
if(e[i] == '('){ cnt += 1; }
else if(e[i] == ')'){ cnt -= 1; }
}
}
void print(vector<string> t){
for(int i = 0; i < t.size(); i++){
cout << t[i] << " ";
}
cout << endl;
}
int evaluate(string expression) {
map<string, int> globvar;
vector<string> tokens;
getTokens(expression, tokens);
assert(!tokens.empty());
string cmd = tokens[0];
assert(cmd == "let" || cmd == "add" || cmd == "mult");
//print(tokens);
int res = 0;
if(cmd == "let"){
int i = 1;
assert(tokens.size() >= 4);
for( ; i + 1 < tokens.size(); i += 2){
string v1 = tokens[i], e1 = tokens[i + 1];
if(globvar.find(v1) == globvar.end()){
if(var.find(v1) != var.end()){
globvar[v1] = var[v1];
}
else { globvar[v1] = inf; }
}
if(e1[0] == '('){
var[v1] = evaluate(e1);
}
else if(e1[0] >= '0' && e1[0] <= '9' || e1.size() >= 2 && e1[1] >= '0' && e1[1] <= '9'){ var[v1] = stoi(e1); }
else { var[v1] = var[e1]; }
}
string expr = tokens[i];
if(expr[0] == '('){
res = evaluate(expr);
}
else {
if(var.find(expr) != var.end()){ res = var[expr]; }
else { res = stoi(expr); }
}
}
else if(cmd == "add" || cmd == "mult"){
assert(tokens.size() == 3);
string v1 = tokens[1], v2 = tokens[2];
int vint1, vint2;
if(v1[0] == '('){ vint1 = evaluate(v1); }
else {
if(var.find(v1) != var.end()) { vint1 = var[v1]; }
else { vint1 = stoi(v1); }
}
if(v2[0] == '('){ vint2 = evaluate(v2); }
else {
if(var.find(v2) != var.end()) { vint2 = var[v2]; }
else { vint2 = stoi(v2); }
}
if(cmd == "add") { res = vint1 + vint2; }
else if(cmd == "mult") { res = vint1 * vint2; }
}
for(auto it = globvar.begin(); it != globvar.end(); it++){
if(it->second == inf){ var.erase(it->first); }
else{ var[it->first] = it->second; }
}
return res;
}
};