Clojure replace operator with your answer

I am trying to create a function that takes the expression "tree" as its argument and returns a tree with the corresponding calculated values ​​instead of operators.

An example of how a tree might look:

(* (+ 10 (* 4 9)) (- 6 10)) 

And the function should return:

 (-184 (46 10 (36 4 9)) (-4 6 10)) 

If someone could provide me with a solution or two and explain how they work to point me in the right direction, that would be great.

 (def a '(* (+ 5 (* 3 7)) (- 6 8)) ) (defn evaltree [tree] (cons (eval (first (rest tree))) tree)) 

- that’s all I have so far. It analyzes the first part of the list, but does not re-check the rest of the list and does not replace the operator, it only adds the value to the beginning.

+5
source share
3 answers

The functions in clojure.walk are useful when you want to update arbitrary nested data structures, for this case, the following solution seems to work.

 (require '[clojure.walk :as w]) (defn op->answer [expr] (if (list? expr) (cons (eval expr) (rest expr)) expr)) (w/prewalk op->answer '(* (+ 10 (* 4 9)) (- 6 10))) ;;=> (-184 (46 10 (36 4 9)) (-4 6 10)) 

clojore.walk / prewalk performs a preliminary traversal of the expression tree and replaces each node with the return value from your function. You can see the order or calls with the following snippet.

 (w/prewalk #(do (println %) %) '(* (+ 10 (* 4 9)) (- 6 10))) ;; => prints the following (* (+ 10 (* 4 9)) (- 6 10)) * (+ 10 (* 4 9)) + 10 (* 4 9) * 4 9 (- 6 10) - 6 10 
+2
source

Not so difficult: as usual with such evaluators, you have two cases that you need to distinguish: self-evaluation of values ​​and calls (application-function).

 (defn evaluate [expression] (if (seq? expression) ;any sequence is a call with the operator in the first position (evaluate-call expression) expression)) 

Call evaluation is performed by first evaluating the operands / arguments. If one of them is a challenge in itself, we get the sequence back. In the first position of this sequence will be - by induction - the result of the expression (since it has already been evaluated).

 (defn evaluate-call [expression] (let [arguments (map evaluate (rest expression))] ; evaluate arguments (cons (apply (get *functions* (first expression)) ; get function to apply (map #(if (seq? %) (first %) %) arguments)) ; extract result from evaluated result, if neccessary arguments))) 

Finally, we put our result in sequence along with the arguments (evaluated) (this is what cons does).

Of course, we also need to determine the available functions:

 (def ^:dynamic *functions* {'+ + '- - '* * '/ /}) 

Running this code in CIDER (sorry, I can not get ideone to work with clojure code):

 evaluating.core> (evaluate '(* (+ 10 (* 4 9)) (- 6 10))) ;;=> (-184 (46 10 (36 4 9)) (-4 6 10)) 
+2
source

A simple recursive solution. You can return more than one value from a function; I use this by returning a pair: the value and extension you are looking for.

The structure is how you are likely going to create a lisp interpreter, in which case you will not need a second return value and just return the value of the expression.

Here are the functions. reval* is the implementation, and reval expands the result:

 (defn reval* [form] (cond (number? form) [form form] (list? form) (let [[op & rest] form [vs forms] (->> rest (map reval*) (apply map list)) v (eval form)] [v (conj forms v)]) :else (throw (RuntimeException. "Unsupported form")))) (defn reval [form] (second (reval* form))) 

Please note that I used eval as a shortcut, but when creating the interpreter you would probably use a hash map similar to what @Daniel Jour did - *functions and use (apply (*functions* op) vs)

0
source

Source: https://habr.com/ru/post/1235957/


All Articles