736 Lisp 语法解析(括号嵌套-递归)

1. 问题描述:

给定一个类似 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)
链接:https://leetcode-cn.com/problems/parse-lisp-expression

2. 思路分析:

分析题目可以知道这道题目类似于力扣的726题,都是属于括号嵌套的问题,对于这一类括号嵌套的问题一般使用递归解决即可,因为括号嵌套本身就是一个递归的过程,我们在递归求解表达式结果的时候先求解出最里层的括号表达式的值,然后返回到上一层继续求解直到求解到最外层的括号为止。Lisp表达式其实也是一个递归的过程,所以可以使用递归来求解。首先表达式expression为一个括号序列,所以一开始需要跳过左括号,然后截取前三个字符判断是哪一种操作:let/add/mult,对于let操作存在多组变量与表达式的值,并且最后一个表达式的值为let语句的结果,所以我们求解出这些变量对应的值之后将其存储到一个哈希表中,对于add操作我们需要求解出两个加数的值,两个加数相加就是add的结果,对于mult操作我们则需要求解出两个乘数,两个乘数相乘就是mult的结果,我们需要声明一个全局的变量k来记录递归的位置,这样当我们返回到上一层的时候当前递归的位置是最新的,在求解的时候需要跳过空格或者括号这些字符,另外这道题目还涉及到一个作用域的问题,可以发现一个括号里面的变量的作用域在整个括号之内,当出去了括号之后那么当前的变量对括号外的变量是没有影响的,所以括号内变量的作用域是在整个括号之内的;而我们在求解出变量对应的值之后存储到哈希表中(这里使用的是python语言所以使用字典来存储键值对的值),而且一个括号属于一个作用域所以当我们在使用get方法求解括号表达式之前,使用python中的copy方法将字典拷贝一份这样当我们往下递归调用的时候不会影响到上一个括号内键值对的值,copy方法属于深拷贝,只会拷贝到父目录而这道题目恰好是一个键对应一个值,所以括号内的对于字典的修改不会影响到括号外的键值对的值,其实就是在求解括号表达式的之前拷贝了一个字典的副本。

3. 代码如下:

class Solution:
    k = 0

    def get(self, s: str, dic: dict):
        val = 0
        if s[self.k] == "-" or "0" <= s[self.k] <= "9":
            # 求解对应的数字
            u = self.k + 1
            while "0" <= s[u] <= "9": u += 1
            val = int(s[self.k: u])
            self.k = u
        # 当前的表达式是一个变量我们求出变量的名字之后再哈希表中取出即可
        elif s[self.k] != "(":
            name = ""
            u = self.k
            while s[u] != ")" and s[u] != " ": u += 1
            name = s[self.k: u]
            self.k = u
            val = dic[name]
        # 当前的表达式是一个括号表达式, 则需要调用dfs方法求出对应的值
        else:
            # 往下递归调用之前需要先拷贝一份字典的副本, 这样递归调用的时候对键值对的修改不会影响到当前括号外的键值对的值
            dic2 = dic.copy()
            t = self.dfs(s, dic2)
            return t
        return val

    def dfs(self, s: str, dic: dict):
        val = 0
        # 跳过当前的左括号
        self.k += 1
        # 截取前三个字符判断属于哪一种操作
        t = s[self.k: self.k + 3]
        if t == "let":
            # 跳过let和空格
            self.k += 4
            # 一组一组处理v e的对, 将这些键值对存储到字典(哈希表)中
            while self.k < len(s):
                name = ""
                # 当前不是空格有右括号的时候找到最末尾的v对应的名字
                while s[self.k] != " " and s[self.k] != ")":
                    name += s[self.k]
                    self.k += 1
                # 当前是右括号说明结束了
                if s[self.k] == ")":
                    val = dic[name]
                    break
                # 跳过空格
                self.k += 1
                # 求解v的值, v可以是一个数字也可以是一个表达式所以可以使用一个方法来求解出对应的e的值
                val = self.get(s, dic)
                # 跳过空格
                self.k += 1
                # 将键值对存储到哈希表中
                dic[name] = val
                # 判断是否是let语句的最后一个表达式, 最后一个表达式可以是-/数字或者是左括号
                if s[self.k] == "(" or s[self.k] == "-" or "0" <= s[self.k] <= "9":
                    val = self.get(s, dic)
                    break
        # 当前的操作是add
        elif t == "add":
            # 跳过add和空格
            self.k += 4
            # 求解第一个加数的值
            a = self.get(s, dic)
            # 跳过空格
            self.k += 1
            # 求解第二个加数的值
            b = self.get(s, dic)
            val = a + b
        else:
            # 跳过mult和空格
            self.k += 5
            a = self.get(s, dic)
            self.k += 1
            b = self.get(s, dic)
            val = a * b
        # 跳过当前匹配的右括号
        self.k += 1
        return val

    def evaluate(self, s: str) -> int:
        dic = dict()
        return self.dfs(s, dic)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值