You are given a string expression
representing a Lisp-like expression to return the integer value of.html
The syntax for these expressions is given as follows. git
(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
.(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
.(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
.Evaluation Examples:express
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:ide
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.expression
is at most 2000. (It is also non-empty, as that would not be a legal expression.)
這道題讓咱們解析Lisp語言的表達式,之前據說過Lisp語言,可是徹底沒有接觸過,看了題目中的描述和給的例子,感受很叼。估計題目只讓咱們處理一些簡單的狀況,畢竟不可能讓咱們寫一個編譯器出來。題目中說了給定的表達式都是合法的,這樣也下降了難度。還有一個好的地方是題目給了充足的例子,讓咱們去更好的理解這門新的語言。咱們經過分析例子發現,全部的命令都是用括號來包裹的,並且裏面還能夠嵌套小括號即子命令。讓咱們處理的命令只有三種,add,mult,和let。其中add和mult比較簡單就是加法和乘法,就把後面兩個數字或者子表達式的值加起來或成起來便可。let命令稍稍麻煩一些,後面能夠跟好多變量或表達式,最簡單的是三個,通常第一個是個變量,好比x,後面會跟一個數字或子表達式,就是把後面的數字或子表達式的值賦值給前面的變量,第三個位置是個表達式,其值是當前let命令的返回值。還有一個比較重要的特性是外層的變量值不會隨着裏層的變量值改變,好比對於下面這個例子:函數
(let x 2 (add (let x 3 (let x 4 x)) x))
剛開始x被賦值爲2了,而後在返回值表達式中,又有一個add操做,add操做的第一個變量又是一個子表達式,在這個子表達式中又定義了一個變量x,並複製爲3,再其返回值表達式又定義了一個變量x,賦值爲4,並返回這個x,那麼最內層的表達式的返回值是4,那麼x被賦值爲3的那層的返回值也是4,此時add的第一個數就是4,那麼其第二個x是多少,其實這個x並無被裏層的x的影響,仍然是剛開始賦值的2,那麼咱們就看出特色了,外層的變量是能影響裏層變量的,而裏層變量沒法影響外層變量。那麼咱們只要在遞歸的時候不加引用就好了,這樣值就不會在遞歸函數中被更改了。post
對於這種長度不定且每一個可能包含子表達式的題,遞歸是一個很好的選擇,因爲須要給變量賦值,因此須要創建一個變量和其值之間的映射,而後咱們就要來寫遞歸函數了,最開始咱們給定的表達式確定是有括號的,因此咱們先處理這種狀況,括號對於咱們的解析沒有用,因此要去掉首尾的括號,而後咱們用一個變量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必定不能加引用,具體能夠參見上面那個例子的分析,加了引用後外層的變量值就會受內層的影響,這是不符合題意的,參見代碼以下:this
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[0] == '-' || (str[0] >= '0' && str[0] <= '9')) return stoi(str); else if (str[0] != '(') return m[str]; string s = str.substr(1, str.size() - 2); int cur = 0; 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 + 1, t = cur, cnt = 1; if (s[cur] == '(') { while (cnt != 0) { if (s[end] == '(') ++cnt; else if (s[end] == ')') --cnt; ++end; } } else { while (end < s.size() && s[end] != ' ') ++end; } cur = end + 1; return s.substr(t, end - t); } };
相似題目:lua
參考資料:spa
https://discuss.leetcode.com/topic/112079/c-recursion-solution-with-explaination/2