如何写一个递归fn来评估下面的结构

时间:2013-04-23 17:17:34

标签: clojure

我有一个结构,其中第一个元素是一个函数,其余的元素是fn。现在每个arg可以反过来成为具有类似charectristic的向量。

[+ 1 2 3 [- 4 3] 5 6 [- 9 8 [+ 5 6]] 4 5]

如何在给定任何此类向量的情况下编写递归fn来计算结果?还有什么方法可以评估它,因为如果向量可以被()替换,结构将是一个有效的clojure形式?

2 个答案:

答案 0 :(得分:5)

user=> (def d '[+ 1 2 3 [- 4 3] 5 6 [- 9 8 [+ 5 6]] 4 5])
#'user/d
user=> (defn to-list [elt]
  #_=>   (if (vector? elt) (map to-list elt) elt))
user=> (to-list d)
(+ 1 2 3 (- 4 3) 5 6 (- 9 8 (+ 5 6)) 4 5)
user=> (eval *1)
17

答案 1 :(得分:1)

值得注意的是eval将花费相当多的时间在这里:

;; to-list as in Michiel's answer, v is the example from the question
user=> (time (dotimes [_ 100] (eval (to-list v))))
"Elapsed time: 192.098235 msecs"

简单的自定义函数可以快一个数量级:

user=> (defn calcvec [v]
         (if (vector? v)
           (apply (resolve (first v))
                  (map calcvec (next v)))
           v))
#'user/calcvec
user=> (calcvec v)
17
user=> (time (dotimes [_ 100] (calcvec v)))
"Elapsed time: 15.87096 msecs"

我尝试使用Criterium对这些进行基准测试,但是quick-bencheval版本上花费了很长时间 ,所以我最终杀了它。 (尽管如此,calcvec也没问题。)

一般来说,eval对于很少执行的操作(可能只有一次)最有意义,例如从动态构造的代码片段等编译函数。所以,如果你只需要计算值少数这样的向量,eval方法很好;否则你会用calcvec或类似的东西变得更好。毫无疑问,eval版本可以做得更多,例如它将处理特殊形式和宏。