2013-04-23 50 views
1

我有一個結構,其中第一個元素是一個函數,其餘的參數指向fn。現在每個arg可以反過來是一個具有類似charectristic的向量。如何寫一個遞歸fn來評估下面的結構

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

我如何寫一個遞歸FN給出任何這樣的向量計算出結果?還有什麼方法來評估它,因爲如果向量可以用()替換,結構將是一個有效的clojure形式?

回答

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

值得一提的是,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-bench需要方式太長了eval版本所以我最終殺了它。 (雖然它與calcvec沒有關係)。

一般而言,eval對於很少執行操作(可能只有一次)的操作是最有意義的,比如從一個動態構建的代碼塊編譯一個函數等等。所以,如果你只需要計算一些這樣的向量的值,eval方法是好的;否則你會更好用calcvec或類似的東西。毫無疑問,eval版本可以做得更多,例如它可以處理特殊的表單和宏。