You are given a string expression representing a Lisp-like expression to return the integer value of.

The syntax for these expressions is given as follows.

  • An expression is either an integer, a let-expression, an add-expression, a mult-expression, or an assigned variable. Expressions always evaluate to a single integer.
  • (An integer could be positive or negative.)
  • A let-expression takes the form (let v1 e1 v2 e2 ... vn en expr), where let is always the string "let", then there are 1 or more pairs of alternating variables and expressions, meaning that the first variable v1 is assigned the value of the expression e1, the second variable v2 is assigned the value of the expression e2, and so on sequentially; and then the value of this let-expression is the value of the expression expr.
  • An add-expression takes the form (add e1 e2) where add is always the string "add", there are always two expressions e1, e2, and this expression evaluates to the addition of the evaluation of e1 and the evaluation of e2.
  • A mult-expression takes the form (mult e1 e2) where mult is always the string "mult", there are always two expressions e1, e2, and this expression evaluates to the multiplication of the evaluation of e1 and the evaluation of e2.
  • For the purposes of this question, we will use a smaller subset of variable names. A variable starts with a lowercase letter, then zero or more lowercase letters or digits. Additionally for your convenience, the names "add", "let", or "mult" are protected and will never be used as variable names.
  • Finally, there is the concept of scope. When an expression of a variable name is evaluated, within the context of that evaluation, the innermost scope (in terms of parentheses) is checked first for the value of that variable, and then outer scopes are checked sequentially. It is guaranteed that every expression is legal. Please see the examples for more details on scope.

Evaluation Examples:

Input: (add 1 2)
Output: 3 Input: (mult 3 (add 2 3))
Output: 15 Input: (let x 2 (mult x 5))
Output: 10 Input: (let x 2 (mult x (let x 3 y 4 (add x y))))
Output: 14
Explanation: In the expression (add x y), when checking for the value of the variable x,
we check from the innermost scope to the outermost in the context of the variable we are trying to evaluate.
Since x = 3 is found first, the value of x is 3. Input: (let x 3 x 2 x)
Output: 2
Explanation: Assignment in let statements is processed sequentially. Input: (let x 1 y 2 x (add x y) (add x y))
Output: 5
Explanation: The first (add x y) evaluates as 3, and is assigned to x.
The second (add x y) evaluates as 3+2 = 5. Input: (let x 2 (add (let x 3 (let x 4 x)) x))
Output: 6
Explanation: Even though (let x 4 x) has a deeper scope, it is outside the context
of the final x in the add-expression. That final x will equal 2. Input: (let a1 3 b2 (add a1 1) b2)
Output 4
Explanation: Variable names can contain digits after the first character.

Note:

  • The given string expression is well formatted: There are no leading or trailing spaces, there is only a single space separating different components of the string, and no space between adjacent parentheses. The expression is guaranteed to be legal and evaluate to an integer.
  • The length of expression is at most 2000. (It is also non-empty, as that would not be a legal expression.)
  • The answer and all intermediate calculations of that answer are guaranteed to fit in a 32-bit integer.

这道题让我们解析Lisp语言的表达式,以前听说过Lisp语言,但是完全没有接触过,看了题目中的描述和给的例子,感觉很叼。估计题目只让我们处理一些简单的情况,毕竟不可能让我们写一个编译器出来。题目中说了给定的表达式都是合法的,这样也降低了难度。还有一个好的地方是题目给了充足的例子,让我们去更好的理解这门新的语言。我们通过分析例子发现,所有的命令都是用括号来包裹的,而且里面还可以嵌套小括号即子命令。让我们处理的命令只有三种,add,mult,和let。其中add和mult比较简单就是加法和乘法,就把后面两个数字或者子表达式的值加起来或成起来即可。let命令稍稍麻烦一些,后面可以跟好多变量或表达式,最简单的是三个,一般第一个是个变量,比如x,后面会跟一个数字或子表达式,就是把后面的数字或子表达式的值赋值给前面的变量,第三个位置是个表达式,其值是当前let命令的返回值。还有一个比较重要的特性是外层的变量值不会随着里层的变量值改变,比如对于下面这个例子:

(let x  (add (let x  (let x  x)) x))

刚开始x被赋值为2了,然后在返回值表达式中,又有一个add操作,add操作的第一个变量又是一个子表达式,在这个子表达式中又定义了一个变量x,并复制为3,再其返回值表达式又定义了一个变量x,赋值为4,并返回这个x,那么最内层的表达式的返回值是4,那么x被赋值为3的那层的返回值也是4,此时add的第一个数就是4,那么其第二个x是多少,其实这个x并没有被里层的x的影响,仍然是刚开始赋值的2,那么我们就看出特点了,外层的变量是能影响里层变量的,而里层变量无法影响外层变量。那么我们只要在递归的时候不加引用就行了,这样值就不会在递归函数中被更改了。

对于这种长度不定且每个可能包含子表达式的题,递归是一个很好的选择,由于需要给变量赋值,所以需要建立一个变量和其值之间的映射,然后我们就要来写递归函数了,最开始我们给定的表达式肯定是有括号的,所以我们先处理这种情况,括号对于我们的解析没有用,所以要去掉首尾的括号,然后我们用一个变量cur表示当前指向字符的位置,初始化为0,下面要做的就是先解析出命令单词,我们调用一个子函数parse,在parse函数中,简单的情况就是解析出add,mult,或let这三个命令单词,我们用一个指针来遍历字符,当越界或遇到空格就停止,但是如果我们需要解析的是个子表达式,而且里面可能还有多个子表达式,那么我们就需要找出最外面这个左括号对应的右括号,因为中间可能还会有别的左右括号,里面的内容就再之后再次调用递归函数时处理。判断的方法就是利用匹配括号的方法,用变量cnt来表示左括号的的个数,初始化为1,当要parse的表达式第一个字符是左括号时,进入循环,循环条件是cnt不为0,当遇到左括号时cnt自增1,反之当遇到右括号时cnt自减1,每次指针end都向右移动一个,最后我们根据end的位置减去初始时cur的值(保存在变量t中),可以得到表达式。如果解析出的是命令let,那么进行while循环,然后继续解析后面的内容,如果此时cur大于s的长度了,说明此时是let命令的最后一个部分,也就是返回值部分,直接调用递归函数返回即可。否则就再解析下一个部分,说明此时是变量和其对应值,我们要建立映射关系。如果之前解析出来的是add命令,那么比较简单,就直接解析出后面的两个部分的表达式,并分别调用递归函数,将递归函数的返回值累加并返回即可。对于mult命令同样的处理方式,只不过是将两个递归函数的返回值乘起来并返回。然后我们再来看如果表达式不是以左括号开头的,说明只能是数字或者变量,那么先来检测数字,如果第一个字符是负号或者0到9之间的数字,那么直接将表达式转为int型即可;否则的话就是变量,我们直接从哈希map中取值即可。最后需要注意的就是递归函数的参数哈希map一定不能加引用,具体可以参见上面那个例子的分析,加了引用后外层的变量值就会受内层的影响,这是不符合题意的,参见代码如下:

class Solution {
public:
int evaluate(string expression) {
unordered_map<string, int> m;
return helper(expression, m);
}
int helper(string str, unordered_map<string, int> m) {
if (str[] == '-' || (str[] >= '' && str[] <= '')) return stoi(str);
else if (str[] != '(') return m[str];
string s = str.substr(, str.size() - );
int cur = ;
string cmd = parse(s, cur);
if (cmd == "let") {
while (true) {
string var = parse(s, cur);
if (cur > s.size()) return helper(var, m);
string t = parse(s, cur);
m[var] = helper(t, m);
}
} else if (cmd == "add") {
return helper(parse(s, cur), m) + helper(parse(s, cur), m);
} else if (cmd == "mult") {
return helper(parse(s, cur), m) * helper(parse(s, cur), m);
}
}
string parse(string& s, int& cur) {
int end = cur + , t = cur, cnt = ;
if (s[cur] == '(') {
while (cnt != ) {
if (s[end] == '(') ++cnt;
else if (s[end] == ')') --cnt;
++end;
}
} else {
while (end < s.size() && s[end] != ' ') ++end;
}
cur = end + ;
return s.substr(t, end - t);
}
};

类似题目:

Ternary Expression Parser

参考资料:

https://discuss.leetcode.com/topic/112079/c-recursion-solution-with-explaination/2

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Parse Lisp Expression 解析Lisp表达式的更多相关文章

  1. Java实现 LeetCode 736 Lisp 语法解析(递归)

    736. Lisp 语法解析 给定一个类似 Lisp 语句的表达式 expression,求出其计算结果. 表达式语法如下所示: 表达式可以为整数,let 语法,add 语法,mult 语法,或赋值的 ...

  2. thymeleaf+layui加载页面渲染时TemplateProcessingException: Could not parse as expression

    Caused by: org.attoparser.ParseException: Could not parse as expression: " {type: 'numbers'}, { ...

  3. layui表格数据渲染SpringBoot+Thymeleaf返回的数据时报错(Caused by: org.attoparser.ParseException: Could not parse as expression: ")

    layui table渲染数据时报错(Caused by: org.attoparser.ParseException: Could not parse as expression: ") ...

  4. 处理 javax.el.ELException: Failed to parse the expression 报错

    在JSP的表达式语言中,使用了  <h3>是否新Session:${pageContext.session.new}</h3>  输出Session是否是新的,此时遇到了  j ...

  5. EnjoyingSoft之Mule ESB开发教程第四篇:Mule Expression Language - MEL表达式

    目录 1. MEL的优势 2. MEL的使用场景 3. MEL的示例 4. MEL的上下文对象 5. MEL的Variable 6. MEL访问属性 7. MEL操作符 本篇主要介绍Mule表达式语言 ...

  6. Expression Template(表达式模板,ET)

    1.前言 在前一篇文章自己实现简单的string类中提到在实现+操作符重载函数时,为了防止返回时生成的临时对象调用拷贝构造函数动态申请内存空间,使用了一个叫move的函数,它是C++0x新增的特性.既 ...

  7. 解决JSP 不解析EL表达式

    解决JSP 不解析EL表达式,jsp在使用EL表达式的时候发现它不被解析,而是直接以字符串的形式显示了出来,经过查阅资料和实践,终于得知了原因并找到了解决方案 原因是:在默认情况下,Servlet 2 ...

  8. The Lisp Curse /Lisp魔咒

    The Lisp Curse /Lisp魔咒 http://winestockwebdesign.com/Essays/Lisp_Curse.html 英文出处 http://www.soimort. ...

  9. EL(Expression Language)表达式语言

    EL(Expression Language)表达式语言 EL的基本语法是以${开始,以}结束 为了能够方便地输出数据,EL提供了11个内置对象,其中 2个内置对象为了方便输出请求参数 param用来 ...

随机推荐

  1. Online database documentation.

    贫道2018年1月正式出道,可以说在IT界我就是个菜鸟.但我有着一颗不服输的心,我相信我会在这条路走上巅峰之道的.下面我来写我的第一份学习笔记: 介绍:大多数公司都有自己的数据文档,估计大多数都是用P ...

  2. 阿里聚安全·安全周刊】一种秘密窃取数据的新型 Android 木马|iOS 11相机惊现BUG

    本周的七个关键词:  新型 Android 木马丨 TLS 1.3 丨  阿里安全图灵实验室 丨 漏洞感染 Linux 服务器 丨 CPU曝极危漏洞 丨   iOS 11相机BUG 丨R2D2技术 - ...

  3. c语言程序设计第4周编程练习(素数和)

    1 素数和(5分) 题目内容: 我们认为2是第一个素数,3是第二个素数,5是第三个素数,依次类推. 现在,给定两个整数n和m,0<n<=m<=200,你的程序要计算第n个素数到第m个 ...

  4. 从Firefox升级说学习方法

    今天早上,打开PortableAPPs时,它提示我升级FireFox,跟往常一样我没考虑就升级了. 打开Firefox 57神速,很是惊喜,打开后发现悲剧了,自己(通过下载插件)定制的功能都不能使用了 ...

  5. C语言程序设计第三次作业--选择结构(1)

    Deadline: 2017-10-29 22:00 一.学习要点 掌握关系运算符和关系表达式 掌握如何判断两个实数相等 掌握常用数学函数的使用 掌握逻辑运算符和逻辑表达式 理解逻辑运算的短路特性 掌 ...

  6. hibernate框架学习错误集锦-org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL)

    最近学习ssh框架,总是出现这问题,后查证是没有开启事务. 如果采用注解方式,直接在业务层加@Transactional 并引入import org.springframework.transacti ...

  7. XCode Build Settings中几种Search Paths

    Header search path:去查找头文件的路径,同在在你需要使用第三方库的时候,在这里设置你的头文件路径目录,如图 <code><span class="str& ...

  8. 【iOS】OC-Quartz2D简单使用

    什么是Quartz2D Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统 作用 ? 1 2 3 4 5 6 7 8 9 <code>Quartz 2D能完成的工作 绘制图形 ...

  9. php的格式化数字函数

    php格式化数字:位数不足前面加0补足 php格式化数字:位数不足前面加0补足 感谢:http://www.cnblogs.com/xiaochaohuashengmi/archive/2011/12 ...

  10. Python爬虫之urllib模块2

    Python爬虫之urllib模块2 本文来自网友投稿 作者:PG-55,一个待毕业待就业的二流大学生. 看了一下上一节的反馈,有些同学认为这个没什么意义,也有的同学觉得太简单,关于Beautiful ...