clojure将运算符替换为其答案

clojure将运算符替换为其答案,clojure,lisp,Clojure,Lisp,我试图生成一个函数,该函数以表达式“tree”为参数,返回树,并用相关的计算值代替运算符 一棵树可能是什么样子的一个例子是: (* (+ 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 (fir

我试图生成一个函数,该函数以表达式“tree”为参数,返回树,并用相关的计算值代替运算符

一棵树可能是什么样子的一个例子是:

(* (+ 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)) 

这是我到目前为止所拥有的一切。它会计算列表的第一部分,但不会递归到列表的其余部分,也不会替换运算符,它只会将值添加到开头。

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

这并不难:像往常一样,对于这种计算器,您需要区分两种情况:自评估值和调用(函数应用程序)

计算调用的方法是首先计算操作数/参数。如果其中一个本身就是一个调用,那么我们将得到一个序列。在这个序列的第一个位置,通过归纳,将是表达式的结果(因为它已经被计算过)

最后,我们将结果与(已计算的)参数一起打包成一个序列(这就是
cons
所做的)

当然,我们还需要在某个地方定义可用的函数:

(def ^:dynamic *functions*
  {'+ +
   '- -
   '* *
   '/ /})
在苹果酒中运行此代码(很抱歉,无法让ideone使用clojure代码)会给我带来:

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

一个简单的递归解决方案。您可以从一个函数返回多个值;我通过返回一对来利用它:您正在寻找的值和扩展

该结构是您可能创建lisp解释器的方式,在这种情况下,您不需要第二个返回值,只需要返回表达式的值

下面是函数
reval*
是实现,并且
reval
打开结果:

(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
作为快捷方式,但在创建解释器时,您可能会使用类似于@Daniel Jour所做的hashmap--
*函数
,而使用
(apply(*functions*op)vs)

到目前为止您尝试了什么?如果您能展示一些代码,我们可以帮助您发现其中的问题。StackOverflow不是一个代码编写服务。(def a'(*(+5(*37))(-68))(defn evaltree[tree](cons(eval(first(rest tree)))tree))到目前为止,我所拥有的用于计算list@stevegarrid将代码添加到帖子中,而不是作为注释。这是一个相当棘手的问题。我的第一个想法是让一个函数工作,它对一个S表达式进行描述。这就像
(cons(eval树)(rest树))
一样简单。我认为下一步应该是在整个树上递归地应用它,但是这不起作用,因为我们将从内到外转换树,所以外层将查看类似于
(2 1)的内容
这是不可评估的。您可以尝试的一种方法是从头开始构建一个新的树,从输入树的内部开始,但这听起来很复杂。IMHO,我想如果你刚刚开始学习Lisp,这类问题可能不是最好的开始。Evaluate call似乎不适合我?它表示无法在此上下文中解析求值调用。有什么原因吗?感谢这些函数相互调用,我想您需要
(声明…
第二个定义的函数。但是,这仅在应用于纯函数语言时在语义上是正确的。因为您的代码多次计算内部表达式,所以副作用会发生多次。这也会降低性能。如果性能或副作用是一个问题,您可以使用postwalk轻松地重新编写解决方案,只需稍微增加复杂性。一般来说,对于这类问题,您应该更喜欢高阶函数,它们通常更简单,更易于维护,并且遵循“最小意外原则”。由于示例中的运算符集受到约束,因此副作用不是问题,预走解决方案稍微简单一些,因此避免过早优化始终是一个好主意我同意您的观点,即人们应该更喜欢高阶函数,但我不会将其称为“过早优化”:看到这样的表达式时,代码的行为与预期不同,因为它没有与Lisp(like)语言本身相同(甚至相似)的求值规则。因此,国际海事组织认为,这没有遵循最不出人意料的原则。
evaluating.core> (evaluate '(* (+ 10 (* 4 9)) (- 6 10)))
;;=> (-184 (46 10 (36 4 9)) (-4 6 10))
(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)))