736. Parse Lisp Expression

Difficulty:
Related Topics:
Similar Questions:

Problem

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.

  Example 1:

Input: expression = "(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.

Example 2:

Input: expression = "(let x 3 x 2 x)"
Output: 2
Explanation: Assignment in let statements is processed sequentially.

Example 3:

Input: expression = "(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.

  Constraints:

Solution (Java)

class Solution {
    static class Exp {
        Deque<Exp> exps;
        String op;
        Exp parent;

        public Exp(Exp from) {
            this.exps = new LinkedList<>();
            this.parent = from;
        }

        private int evaluate(Map<String, Integer> vars) {
            if (op.equalsIgnoreCase("add")) {
                return exps.pop().evaluate(vars) + exps.pop().evaluate(vars);
            } else if (op.equalsIgnoreCase("mult")) {
                return exps.pop().evaluate(vars) * exps.pop().evaluate(vars);
            } else if (op.equalsIgnoreCase("let")) {
                Map<String, Integer> nextVars = new HashMap<>(vars);
                while (exps.size() > 1) {
                    String varName = exps.pop().op;
                    int val = exps.pop().evaluate(nextVars);
                    nextVars.put(varName, val);
                }
                return exps.pop().evaluate(nextVars);
            } else {
                if (Character.isLetter(op.charAt(0))) {
                    return vars.get(op);
                } else {
                    return Integer.parseInt(op);
                }
            }
        }
    }

    private Exp buildTree(String exp) {
        Exp root = new Exp(null);
        Exp cur = root;
        int n = exp.length() - 1;
        while (n >= 0) {
            char c = exp.charAt(n);
            if (c == ')') {
                Exp next = new Exp(cur);
                cur.exps.push(next);
                cur = next;
            } else if (c == '(') {
                cur.op = cur.exps.pop().op;
                cur = cur.parent;
            } else if (c != ' ') {
                int pre = n;
                while (pre >= 0 && exp.charAt(pre) != '(' && exp.charAt(pre) != ' ') {
                    pre--;
                }
                Exp next = new Exp(cur);
                next.op = exp.substring(pre + 1, n + 1);
                cur.exps.push(next);
                n = pre + 1;
            }
            n--;
        }
        return root.exps.pop();
    }

    public int evaluate(String exp) {
        return buildTree(exp).evaluate(new HashMap<>());
    }
}

Solution (C++)

class Solution {
public:
    int evaluate(string expression) {        
        scopes_.clear();
        int pos = 0;
        return eval(expression, pos);
    }
private:
    int eval(const string& s, int& pos) {
        scopes_.push_front(unordered_map<string, int>());
        int value = 0; // The return value of current expr        
        if (s[pos] == '(') ++pos;

        // command, variable or number
        const string token = getToken(s, pos);

        if (token == "add") {
            int v1 = eval(s, ++pos);
            int v2 = eval(s, ++pos);
            value = v1 + v2;
        } else if (token == "mult") {
            int v1 = eval(s, ++pos);
            int v2 = eval(s, ++pos);
            value = v1 * v2;
        } else if (token == "let") {
            string var;
            // expecting " var1 exp1 var2 exp2 ... last_expr)"
            while (s[pos] != ')') {
                ++pos;
                // Must be last_expr
                if (s[pos] == '(') {
                    value = eval(s, ++pos);
                    break;
                }                
                // Get a token, could be "x" or "-12" for last_expr
                var = getToken(s, pos);                
                // End of let, var is last_expr
                if (s[pos] == ')') {
                    if (isalpha(var[0]))
                        value = getValue(var);
                    else
                        value = stoi(var);
                    break;
                }
                // x -12 -> set x to -12 and store in the current scope and take it as the current return value
                value = scopes_.front()[var] = eval(s, ++pos);
            }
        } else if (isalpha(token[0])) {            
            value = getValue(token); // symbol
        } else {            
            value = std::stoi(token); // number
        }
        if (s[pos] == ')') ++pos;        
        scopes_.pop_front();        
        return value;
    }

    int getValue(const string& symbol) {
        for (const auto& scope : scopes_)        
            if (scope.count(symbol)) return scope.at(symbol);
        return 0;
    }

    // Get a token from current pos.
    // "let x" -> "let"
    // "-12 (add x y)" -> "-12"
    string getToken(const string& s, int& pos) {        
        string token;
        while (pos < s.length()) {            
            if (s[pos] == ')' || s[pos] == ' ') break;
            token += s[pos++];
        }
        return token;
    }

    deque<unordered_map<string, int>> scopes_; 
};

Explain:

nope.

Complexity: