我正在尝试制作一个带有表达式'树的函数。作为其参数,并返回具有相关计算值的树代替运算符。
树的外观示例如下:
(* (+ 10 (* 4 9)) (- 6 10))
该函数应返回:
(-184 (46 10 (36 4 9)) (-4 6 10))
如果有人能为我提供一两个解决方案并解释他们如何帮助我指明正确的方向,那就太棒了。
(def a '(* (+ 5 (* 3 7)) (- 6 8)) )
(defn evaltree [tree] (cons (eval (first (rest tree))) tree))
到目前为止,我已经完成了所有工作。它会破坏列表的第一部分,但不会通过执行列表的其余部分并且不替换运算符,它只会将值添加到开头。
答案 0 :(得分:2)
当您想要更新任意嵌套数据结构时,clojure.walk中的函数很有用,以下解决方案似乎适用于这种情况。
(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执行表达式树的预先遍历,并使用函数的返回值替换每个节点。您可以使用以下代码段查看订单或来电。
(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
答案 1 :(得分:2)
难以理解:与此类评估者一样,您需要区分两种情况:自我评估值和调用(函数应用程序)。
(defn evaluate
[expression]
(if (seq? expression) ;any sequence is a call with the operator in the first position
(evaluate-call expression)
expression))
通过首先评估操作数/参数来评估调用。如果其中一个是自己的呼叫,那么我们将得到一个序列。在这个序列的第一个位置将是 - 通过归纳 - 表达式的结果(因为它已经被评估过)。
(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)))
最后,我们将结果与(评估的)参数一起打包成一个序列(这是cons
所做的)。
当然,我们还需要在某处定义我们的可用功能:
(def ^:dynamic *functions*
{'+ +
'- -
'* *
'/ /})
在CIDER中运行此代码(抱歉,无法使用clojure代码),我发现:
evaluating.core> (evaluate '(* (+ 10 (* 4 9)) (- 6 10)))
;;=> (-184 (46 10 (36 4 9)) (-4 6 10))
答案 2 :(得分:0)
一个简单的递归解决方案。您可以从函数返回多个值;我通过返回一对来利用它:您正在寻找的价值和扩张。
结构就是你可能会如何创建一个lisp解释器,在这种情况下你不需要第二个返回值,只返回表达式的值。
这是功能。 (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)))
是实现,eval
打开结果:
*functions
请注意,我使用(apply (*functions* op) vs)
作为快捷方式,但在创建解释器时,您可能会使用类似于@Daniel Jour所做的哈希映射 - {{1}}并使用{{1}而不是。