2011-04-24 23 views
4

我最近開始學習clojure和我正在閱讀Clojure的歡樂去掌握它。我有一個關於在章(8)的代碼段的一個問題,166Unquote拼接和在clojure宏向量中包裹

(defmacro domain [name & body] 
    `{:tag :domain,      ;` 
    :attrs {:name (str '~name)},  ;' 
    :content [[email protected]]}) 

據我瞭解頁面上,body是像第一個除外的所有參數結構的序列。如果是這樣,在第三行中,爲什麼我們不引用拼接([email protected])並將值重新放入向量中。爲什麼不只是做~body而不是[[email protected]]?有什麼不同?

我很抱歉,但我發現真的很難把握整個宏(來自python)。

編輯:有點實驗後,我發現這工作,

(defmacro domain2 [name & body] 
    `{:tag :domain,      ;` 
    :attrs {:name (str '~name)},  ;' 
    :content '~body}) 

,並與我從Joost的的回答得到了結果一起,我想我知道這裏發生了什麼。 body正在列表中,因此如果我沒有在~body之前放置',clojure會嘗試對它進行評估。

user=> (domain "sh" 1 2 3) 
{:content [1 2 3], :attrs {:name "sh"}, :tag :domain} 

user=> (domain2 "sh" 1 2 3) 
{:content (1 2 3), :attrs {:name "sh"}, :tag :domain} 
+0

另外,它可以發佈這樣的書的一段代碼,就像這樣吧? – 2011-04-24 10:57:43

+0

只是一個小事情要注意,它是unquote拼接不uniquote切片 – 2011-04-28 17:37:00

+0

@Nicolas,更新謝謝 – 2011-04-29 01:18:21

回答

2

我認爲答案就在於這個宏的意圖。

快速查看提到的頁面,似乎想法是使用地圖爲域創建數據結構。所選擇的結構與clojure.xml庫使用的結構相同。

確實,emit函數會產生與您的代碼和書中的代碼類似的結果,但是從clojure.xml生成的函數分析會生成一個包含向量中的內容的地圖,最好是執行相同的操作在這裏,爲了不破壞依賴於相同結構的其他代碼。在這裏也使用一個結構來與clojure.xml保持一致可能是個好主意,因爲現在例如(content (domain ...))不起作用。

對數據結構的思考一般來說,我覺得在這裏使用像索引這樣的索引序列是一個好主意,因爲它可以說例如((:content domain-item) 1)來訪問第二項內容。更不用說子序列等了。

+0

謝謝Verneri,這使得很多感覺:) – 2011-05-24 05:35:36

1

你說得很對; 〜body已經是一個序列,所以除非需要保證:content是一個向量(而不僅僅是可以被seq的東西),[〜@ body]表達式可以被〜body替換。

+0

但是,如果我用'〜body'替換'[〜@ body]',並且做'(domain「sh 「1 2 3)',我得到一個'java.lang.ClassCastException:java.lang.Integer不能轉換爲clojure.lang.IFn(NO_SOURCE_FILE:0)'。調用'macroexpand'也會引發同樣的錯誤。 – 2011-04-24 11:34:48

+0

您應該在引用的表達式上調用macroexpand,否則它只會在macroexpand得到它之前進行計算。宏觀展開是一種功能。 – 2011-04-24 11:41:42

+0

@Michiel:哦對!我忘了:) – 2011-04-24 11:57:57