如何評估AST具有更好的性能? 目前我們創建AST作爲樹,其中葉節點(終端)是一個參數的函數 - 關鍵字及其值的映射。終端用關鍵字表示,功能(非終端)可以是用戶(或clojure)定義的功能。生成AST的在Clojure中評估AST(抽象語法樹)
(defn full-growth
"Creates individual by full growth method: root and intermediate nodes are
randomly selected from non-terminals Ns,
leaves at depth depth are randomly selected from terminals Ts"
[Ns Ts arity-fn depth]
(if (<= depth 0)
(rand-nth Ts)
(let [n (rand-nth Ns)]
(cons n (repeatedly (arity-fn n) #(full-growth Ns Ts arity-fn(dec depth)))))))
實施例::完全生長方法從非端子和端子創建樹
=> (def ast (full-growth [+ *] [:x] {+ 2, * 2} 3))
#'gpr.symb-reg/ast
=> ast
(#object[clojure.core$_STAR_ 0x6fc90beb "[email protected]"]
(#object[clojure.core$_STAR_ 0x6fc90beb "[email protected]"]
(#object[clojure.core$_STAR_ 0x6fc90beb "[email protected]"]
:x
:x)
(#object[clojure.core$_PLUS_ 0x1b00ba1a "[email protected]"]
:x
:x))
(#object[clojure.core$_PLUS_ 0x1b00ba1a "[email protected]"]
(#object[clojure.core$_PLUS_ 0x1b00ba1a "[email protected]"]
:x
:x)
(#object[clojure.core$_PLUS_ 0x1b00ba1a "[email protected]"]
:x
:x)))
,這相當於
`(~* (~* (~* ~:x ~:x) (~+ ~:x ~:x)) (~+ (~+ ~:x ~:x) (~+ ~:x ~:x)))
(def ast `(~* (~* (~* ~:x ~:x) (~+ ~:x ~:x)) (~+ (~+ ~:x ~:x) (~+ ~:x ~:x))))
我們可以寫FN直接評估這AST爲:
(defn ast-fn
[{x :x}]
(* (* (* x x) (+ x x)) (+ (+ x x) (+ x x))))
=> (ast-fn {:x 3})
648
我們創造功能兩種方法基於AST,一個與應用和地圖的幫助下,和其他與幫助補償和juxt:
(defn tree-apply
"((+ :x :x) in) => (apply + [(:x in) (:x in))]"
([tree] (fn [in] (tree-apply tree in)))
([tree in]
(if (sequential? tree)
(apply (first tree) (map #(tree-apply % in) (rest tree)))
(tree in))))
#'gpr.symb-reg/tree-apply
=> (defn tree-comp
"(+ :x :x) => (comp (partial apply +) (juxt :x :x))"
[tree]
(if (sequential? tree)
(comp (partial apply (first tree)) (apply juxt (map tree-comp (rest tree))))
tree))
#'gpr.symb-reg/tree-comp
=> ((tree-apply ast) {:x 3})
648
=> ((tree-comp ast) {:x 3})
648
具有定時FN我們測量的時間超過測試用例執行功能:
=> (defn timing
[f interval]
(let [values (into [] (map (fn[x] {:x x})) interval)]
(time (into [] (map f) values)))
true)
=> (timing ast-fn (range -10 10 0.0001))
"Elapsed time: 37.184583 msecs"
true
=> (timing (tree-comp ast) (range -10 10 0.0001))
"Elapsed time: 328.961435 msecs"
true
=> (timing (tree-apply ast) (range -10 10 0.0001))
"Elapsed time: 829.483138 msecs"
true
正如你可以看到有直接的功能(AST-FN)之間的性能差異巨大,樹木補償產生的功能和樹申請生成的函數。
有一些更好的辦法?
編輯: madstap的回答看起來很有希望。我做他的溶液一些修改(端子可以是也有一些其他功能,而不僅僅是關鍵字,如恆定函數不斷返回不論輸入值):
(defn c [v] (fn [_] v))
(def c1 (c 1))
(defmacro full-growth-macro
"Creates individual by full growth method: root and intermediate nodes are
randomly selected from non-terminals Ns,
leaves at depth depth are randomly selected from terminals Ts"
[Ns Ts arity-fn depth]
(let [tree (full-growth Ns Ts arity-fn depth)
val-map (gensym)
ast2f (fn ast2f [ast] (if (sequential? ast)
(list* (first ast) (map #(ast2f %1) (rest ast)))
(list ast val-map)))
new-tree (ast2f tree)]
`{:ast '~tree
:fn (fn [~val-map] ~new-tree)}))
現在,創建AST-M(與使用的常數C1爲終端)和相關AST-M-FN:
時機看起來非常相似:
=> (timing (:fn ast-m) (range -10 10 0.0001))
"Elapsed time: 58.478611 msecs"
true
=> (timing (:fn ast-m) (range -10 10 0.0001))
"Elapsed time: 53.495922 msecs"
true
=> (timing ast-m-fn (range -10 10 0.0001))
"Elapsed time: 74.412357 msecs"
true
=> (timing ast-m-fn (range -10 10 0.0001))
"Elapsed time: 59.556227 msecs"
true
我的答案可能沒有什麼幫助,因爲你可能想在運行時做所有事情,但它幫助我更好地內化了宏如何工作。那謝謝啦。 +1 – madstap