2016-02-02 33 views
0

假設我們有一個在任意位置有_的列表。例如:(a b c _ e f)。我試圖編寫一個宏,對於這樣的列表,找到_並用另一個值(例如,z)替換它:(a b c z e f)寫入替換宏的參數

這是最好的方法是什麼?

回答

6

你確定你需要一個宏嗎?使用replace應該只要你引用列表中很好地工作:

(replace '{_ z} '(a b c _ e f)) ; => (a b c z e f) 
+0

這是優雅的,但我誤解了我的問題:我需要的不是要返回列表,而是要替換,然後評估。在沒有'eval'的ClojureScript中,如何實現這一點? – George

1

的@Josh答案是正確的,首先你要決定你是否真的需要宏觀的東西。我可以想象一個合成的例子。讓我們假設你要定義一個函數,但需要跟蹤一些原因,這_值(也許登錄),使用它是這樣的:

(defn-with-placeholder my-fn [a b c _ e] z 
    (println a b c z e)) 

這是你如何做到這一點:

(defmacro defn-with-placeholder [name args placeholder & body] 
    `(defn ~name ~(vec (replace {'_ placeholder} args)) 
    [email protected])) 

請注意,我使用了前面提出的相同的replace方法。

測試一下在REPL:

user> (defn-with-placeholder my-fn [a b _ d] placeholder 
     (println a b placeholder d)) 
#'user/my-fn 
user> (my-fn 1 2 3 4) 
1 2 3 4 
nil 

確定現在是好看不中用。讓我們與運動走得更遠,並進行了定義,將收集所有省略的參數一些集合(如函數休息參數& args,但在不同的位置) 因此,我們可以定義一個宏defn-with-omitted認爲是這樣的:

(defn-with-omitted my-fn-2 [a _ c _ e f _ h] other-args 
    (println :parameters a c e f h) 
    (println :other-parameters other-args)) 

在REPL:

user> (my-fn-2 1 100 2 200 3 4 300 5) 
:parameters 1 2 3 4 5 
:other-parameters {1 100, 3 200, 6 300} 
nil 

其收集的所有數據省略和它把至other-args地圖,用Arg-位置ARG映射。

要做到這一點,首先,我們需要建立一個處理的arglist並收集所有被省略參數的函數:

(defn process-args [args] 
    (reduce-kv (fn [[args omitted] idx arg] 
       (if (= '_ arg) 
       (let [sym-name (gensym "omitted")] 
        [(conj args sym-name) 
        (assoc omitted idx sym-name)]) 
       [(conj args arg) omitted])) 
      [[] {}] 
      args)) 

這裏就是它的作用:

user> (process-args '[a _ b c _ _ f g]) 
[[a omitted29608 b c omitted29609 omitted29610 f g] 
{1 omitted29608, 4 omitted29609, 5 omitted29610}] 

注意到我在這裏使用gensym,不是爲了隱藏可能的外部定義。

所以現在它很容易使宏:

(defmacro defn-with-omitted [name args omitted-name & body] 
    (let [[args omitted] (process-args args)] 
    `(defn ~name ~args 
     (let [~omitted-name ~omitted] 
     [email protected])))) 

讓我們檢查的擴展:

(defn-with-omitted my-fn-2 [a _ c _ e f _ h] other-args 
    (println :parameters a c e f h) 
    (println :other-parameters other-args)) 

擴展爲:

(defn my-fn-2 [a omitted29623 c omitted29624 e f omitted29625 h] 
    (let [other-args {1 omitted29623, 3 omitted29624, 6 omitted29625}] 
    (println :parameters a c e f h) 
    (println :other-parameters other-args))) 

這正是我們想要的東西。