736. Parse Lisp Expression

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语言的程序。只有let,add,mult三个语句。使用递归的方法进行解析。eval()函数用来计算括号表达式、符号对应的值和数值,括号表达式根据左括号后的单词(let、add或mult)进行let、add或mult操作。要注意的是let赋值的作用域,当前括号内的赋值不影响括号外的值,所以在let()函数开头先记录好变量表,在结束前要恢复该表。


代码:

class Solution {
public:
    int evaluate(string expression) {
        idx = 0;
        expr = expression;
        return eval();
    }

private:
    size_t idx;
    string expr;
    map<string, int> var_table;

    int eval() {
        if(expr[idx] == '(') {
            ++idx;
            switch(expr[idx]) {
                case 'l':
                    return let();
                case 'a':
                    return add();
                case 'm':
                    return mult();
            }
        }
        else if((expr[idx] <= '9' && expr[idx] >= '0') || expr[idx] == '-') {
            size_t offset = 0;
            int val = stoi(expr.substr(idx), &offset);
            idx += offset;
            return val;
        }
        else if(expr[idx] <= 'z' && expr[idx] >= 'a') {
            return var_table[get_var_name()];
        }
        return 0;
    }

    string get_var_name() {
        string var;
        while(expr[idx] != ' ' && expr[idx] != ')') {
            var += expr[idx++];
        }
        return var;
    }

    int let() {
        idx += 4;
        int ret = 0;
        map<string, int> var_table_out = var_table;
        while(true) {
            if(expr[idx] == '(' || (expr[idx] <= '9' && expr[idx] >= '0') || expr[idx] == '-') {
                ret = eval();
                idx++;
                break;
            }
            string var = get_var_name();
            if(expr[idx] == ')') {
                idx++;
                ret = var_table[var];
                break;
            }
            idx++;
            var_table[var] = eval();
            idx++;
            // cout << "let: " << var << " = " << var_table[var] << endl;
        }
        var_table = var_table_out;
        return ret;
    }

    int add() {
        idx += 4;
        int val1 = eval();
        idx++;
        int val2 = eval();
        idx++;
        // cout << "add: " << val1 << " + " << val2 << endl;
        return val1 + val2;
    }

    int mult() {
        idx += 5;
        int val1 = eval();
        idx++;
        int val2 = eval();
        idx++;
        // cout << "mult: " << val1 << " * " << val2 << endl;
        return val1 * val2;
    }
};



### 实现简单LISP算术表达式计算器 为了构建一个简单的LISP算术表达式计算器,可以采用两种主要策略之一:基于栈的方法或是递归解析方法。这里将介绍一种结合两者优点的方式——即使用栈来管理操作数和运算符的同时也通过递归来处理括号内的子表达式。 #### 使用栈与递归相结合的设计方案 该设计遵循以下原则: - **输入预处理**:首先对原始字符串形式的LISP表达式进行初步清理,去除不必要的空白字符并分割成有意义的部分。 - **创建两个栈**:一个是用于存储数值的操作数栈;另一个则是用来保存待执行的操作符栈。当遇到左括号`(`时启动新的递归调用层次,在此期间任何新产生的临时结果都将被压入当前层对应的局部变量中直到匹配到右括号`)`为止[^1]。 - **优先级判断**:每当读取一个新的运算符之前都要先检查其相对于堆顶元素是否有更高的优先级别,如果有,则立即将后者弹出并与最近两次存入的数据一起完成一次实际计算过程,并把所得中间产物重新放回原处等待后续进一步参与其他可能存在的更高级别的运算活动之中去。 - **最终结算**:一旦整个遍历流程结束之后,理论上应该只剩下最后一个尚未解决掉的结果留在那里等着我们取出作为最后的答案输出给用户查看即可。 下面给出一段Python代码片段展示上述逻辑的具体实现方式: ```python from collections import deque def evaluate_lisp_expression(expression): operators = {'+', '-', '*', '/'} def apply_operator(operands, operator): b = operands.pop() a = operands.pop() if operator == '+': return a + b elif operator == '-': return a - b elif operator == '*': return a * b elif operator == '/': return a / b def parse_token(token): try: return float(token), True # 数字返回True标志位表明这是一个数字而非符号 except ValueError: return token, False # 非数字则假定为合法的操作符或分隔符 def eval_recursive(tokens_iter): values_stack = [] ops_stack = [] while True: current_token = next(tokens_iter)[0] if isinstance(current_token, str) and current_token not in operators.union({'(', ')'}): continue value_or_op, is_value = parse_token(current_token) if is_value or (isinstance(value_or_op, tuple)): values_stack.append(value_or_op) elif value_or_op == '(': result_of_subexpr = eval_recursive(tokens_iter) values_stack.append(result_of_subexpr) elif value_or_op == ')': break else: # It's an operator while ops_stack and precedence[value_or_op] <= precedence[ops_stack[-1]]: top_op = ops_stack.pop() res = apply_operator(values_stack, top_op) values_stack.append(res) ops_stack.append(value_or_op) while ops_stack: op = ops_stack.pop() intermediate_result = apply_operator(values_stack, op) values_stack.append(intermediate_result) return values_stack[0] cleaned_expr = ''.join(c for c in expression.replace(' ', '')).strip() # 清理多余空格 tokens_with_sentinel = iter((parse_token(t), t != ')') for t in list(cleaned_expr)) + [(None, False)] # 添加哨兵防止越界访问异常发生 final_result = eval_recursive(iter(tokens_with_sentinel)) return final_result precedence = { '+': 1, '-': 1, '*': 2, '/': 2 } ``` 这段程序实现了对于基本四则运算的支持,并且能够正确处理带有圆括号包围起来的复合表达式部分。当然这只是一个简化版的例子,真实世界里还需要考虑更多边界情况比如非法输入检测等功能完善度上的提升空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值