zoukankan      html  css  js  c++  java
  • 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. 

    Approach #1: String. [Java]

    class Solution {
        public int evaluate(String expression) {
            return evaluate(expression, new HashMap<>());
        }
        
        private int evaluate(String e, Map<String, Deque<Integer>> map) {
            char c = e.charAt(1);   // the expression must start with "(add " or "(mult " or "(let ".
            if (c == 'a') return evaluateAdd(e, map);      // "add" expression
            else if (c == 'm') return evaluateMult(e, map);     // "mult" expression
            else if (c == 'l') return evaluateLet(e, map);      // "let" expression
            else return 0;      // illegal expression so return 0
        }
        
        private int evaluateAdd(String e, Map<String, Deque<Integer>> map) {
            int offset = 5;     // the expression starts with "(add ", so offset starts at 5.
            String o1 = getOperand(e, offset);      // first operand
            
            offset += o1.length() + 1;
            String o2 = getOperand(e, offset);         // second operand
            
            return evaluateOperand(o1, map) + evaluateOperand(o2, map);
        }
        
        private int evaluateMult(String e, Map<String, Deque<Integer>> map) {
            int offset = 6;
            String o1 = getOperand(e, offset);
            
            offset += o1.length() + 1;
            String o2 = getOperand(e, offset);
            
            return evaluateOperand(o1, map) * evaluateOperand(o2, map);
        }
        
        private int evaluateLet(String e, Map<String, Deque<Integer>> map) {
            List<String> variables = new ArrayList<>();     // list of variables assigned in this "let" expression
            int res = 0;        // the result of this "let" expression
            int offset = 5;     // the expression starts with "(let ", so offset starts at 5.
            
            while (offset < e.length()) {
                String o1 = getOperand(e, offset);
                offset += o1.length() + 1;
                
                String o2 = getOperand(e, offset);
                
                if (o2 == null) {       // if second operand is null, we reached the last operand
                    res = evaluateOperand(o1, map);
                    break;
                }
                    
                offset += o2.length() + 1;
    
                variables.add(o1);      // record the variable
    
                if (!map.containsKey(o1)) map.put(o1, new ArrayDeque<>());
    
                map.get(o1).offerFirst(evaluateOperand(o2, map));       // do the assignment
            }
            
            // pop out assigned values before returning from this "let" expression
            for (int i = variables.size() - 1; i >= 0; --i) {
                String variable = variables.get(i);
                Deque<Integer> stack = map.get(variable);
                stack.pollFirst();
                if (stack.isEmpty()) map.remove(variable);
            }
            
            return res;
        }
        
        private String getOperand(String e, int offset) {
            if (offset >= e.length()) return null;      // invalid offset
            
            char c = e.charAt(offset);
            int start = offset;
            
            if (c == '-' || Character.isDigit(c)) {     // operand is an integer
                if (c == '-') offset++;
                while (offset < e.length() && Character.isDigit(e.charAt(offset))) offset++;
            } else if (Character.isLowerCase(c)) {      // operand is a variable
                while (offset < e.length() && Character.isLetterOrDigit(e.charAt(offset))) offset++;
            } else {        // operand is another expression enclosed in parenthses
                for (int cnt = 0; offset < e.length(); ) {
                    c = e.charAt(offset++);
                    if (c == '(') cnt++;
                    if (c == ')') cnt--;
                    if (cnt == 0) break;
                }
            }
            
            return e.substring(start, offset);
        }
        
        private int evaluateOperand(String e, Map<String, Deque<Integer>> map) {
            char c = e.charAt(0);
            
            if (c == '-' || Character.isDigit(c)) {     // operand is an integer so parse it
                return Integer.parseInt(e);
            } else if (Character.isLowerCase(c)) {      // operand is a variable so look it up
                return map.get(e).peekFirst();
            } else {        // operand is another expression so evaluate it recursively
                return evaluate(e, map);
            }
        }
    }
    

      

      

    Analysis:

    These type of problems are notorious for their complex and intertwined nature. A modularized approach would certainly help to clarify the problem. So here is how I would divide the original problem into difference modules.

    For an input string expression, we have the function evaluate to resolve its value.

    The input expression must be one of the following three types of expressions -- add, mult and let. Correspondingly we will have three functions to evaluate each of them --evaluateAdd, evaluateMult and evaluateLet.

    All the three types of expressions can be thought of as composed by operands, where each operand can be an integer, a variable or another expression of the three types mentioned above (note the expression will be enclosed in parentheses).

    An add expression contains 2 operands and is evaluated to be the sum of the two operands.

    A mult expression contains 2 operands and is evaluated to be the product of two operands.

    A let expression contains 2m + 1 operands and is evaluated to be the value of the last operand. The first m pairs of operands correspond to m assignments. For each pair, the first operand is a variable while the second can be an integer, a variable or another expression. To simulate the assignment operations, we will maintain a HashMap, which maps the variable to the assigned values (this also implies the evaluate function in steep 1 should be delegated to a subroutine with an additional HashMap parameter). To simulate the concept of scope, the assigned values will be placed in a stack. Whenever the let expression returns. all assignments performed within it become invalid and should be popped out of the stack.

    From the analyses above, given an expression e, we need to identify its constituent operands. We will have two functions serving for this purpose.

    getOperand: this function will obtain the string representation of the operand starting from the specified offset into the expression e. It will distingguish the three types of operands -- an integer, a variable or another expression (of type add, mult or let).

    evaluateOperand: This function will evaluate the operand string obtained above. For an operand of integer type, it will look up its value in the hash map; for an operand of expression type, it will recurively call the evaluate function to resolve its value.

    Reference:

    https://leetcode.com/problems/parse-lisp-expression/discuss/109718/Java-modularized-solution

    永远渴望,大智若愚(stay hungry, stay foolish)
  • 相关阅读:
    Linux如何通过命令查看日志文件的某几行(中间几行或最后几行)
    将生成200 个激活码(或者优惠券)保存到 oracle关系型数据库中
    将你的 QQ 头像(或者微博头像)右上角加上红色的数字,类似于微信未读信息数量那种提示效果
    面试笔试题:多表关联的update语句、将in中的查询条件按顺序输出和SQL语句增加列、修改列、删除列
    sql中级到高级
    Linux常用命令
    类的特殊成员方法
    正则表达式的方法匹配规则
    启动ecilpse 报错an error has occurred. see the log file
    访问修饰符
  • 原文地址:https://www.cnblogs.com/h-hkai/p/10776148.html
Copyright © 2011-2022 走看看