用于创建地图的Clojure宏

时间:2013-08-11 15:07:49

标签: map macros clojure

我有一个用于创建哈希映射的宏。我应该发送一个mashup的名称和调用函数得到的内容(在这个例子中它是一个函数xx)。

(defmacro data-for-mashup [mashup-name func]  
`(map  {} {:title :content} {~mashup-name (~@func)}))  

宏应该获得任何mashup名称和任何函数,这就是我希望它成为宏的原因。

我告诉它我希望结果看起来像这样

{:title "Events Mashup"
 :content [{ :event-name "event name 1"
                    :performer "performer 1"
                  :date "date 1"
                       :start-time "start time 1"
                       :end-time "end time 1"}
  {:event-name "event name 2"
                       :performer "performer 2"
                      :date "date 2"
                       :start-time "start time 2"
                       :end-time "end time 2"}]}

内容标记的值实际上是地图的矢量。

对宏的调用应该得到像这样的结果

(data-for-mashup "events" (xx))  

在REPL中我得到了这个结果

(["events" #<datamodel$xx mashup_dsl.datamodel$xx@530f0fbd>])  

macroexpand-1导致此

(macroexpand-1 `(data-for-mashup "events" (xx)))  
(clojure.core/map {} {:title :content} {"events" mashup-dsl.datamodel/xx})  

(xx是我的命名空间中的一个函数。)

实际上我希望它扩展到这个

(map {} {:title :content} {"events" (xx)})  

我做错了什么?

2 个答案:

答案 0 :(得分:3)

首先,您是否尝试创建地图调用或哈希映射?

map用于将其第一个arg应用于其他args的每个元素。将{}应用于某些内容将不会生成哈希映射。对于任何单个参数,空的hashmap将返回nil。

user> (map {} [:a :b :c])
(nil nil nil)

作为函数提供给空hashmap的两个参数将始终返回第二个参数。你想在这做什么?

user> ({} :a 0)
0

user> (map {} [:args :here :are-meaningless] (range))
(0 1 2)

接下来,〜@从参数中取出最高级别的嵌套。如果要在列表中返回函数,则需要围绕它构建一个列表。 quote(也写为')将阻止一对parens导致函数的应用,而是构造一个列表。

user> (macroexpand-1 ''(+))
(quote (+))

user> (quote (+))
(+)

user> (defmacro data-for-mashup [mashup-name func]  
        `(map  {} {:title :content} {~mashup-name '(~@func)}))
#'user/data-for-mashup
user> (macroexpand-1 '(data-for-mashup "events" (+)))
(clojure.core/map {} {:title :content} {"events" (quote (+))})
user> (data-for-mashup "events" (+))
(["events" (+)])

这与不使用〜@相同,只是直接使用〜,但引用它返回的内容。

user> (defmacro data-for-mashup [mashup-name func]  
        `(map  {} {:title :content} {~mashup-name '~func}))
#'user/data-for-mashup
user> (macroexpand-1 '(data-for-mashup "events" (+)))
(clojure.core/map {} {:title :content} {"events" (quote (+))})
user> (data-for-mashup "events" (+))
(["events" (+)])

如果您正在尝试构建哈希映射,我认为以下定义之一可能是您想要的:

user> (defmacro data-for-mashup [mashup-name func]  
        `(zipmap [:title :content] [~mashup-name '~func]))
#'user/data-for-mashup
user> (data-for-mashup "events" (+))
{:content (+), :title "events"}

user> (defn data-for-mashup [mashup-name val] (zipmap [:title :content] [mashup-name val]))
#'user/data-for-mashup
user> (data-for-mashup "events" (+))
{:content 0, :title "events"}

答案 1 :(得分:1)

如果我理解你的问题,你可以在这里使用一个函数,而不是宏。我强烈建议您尽可能使用功能。它们更易于组合,更易于阅读。

;; This mashup function is a higher-order function
;; It takes *another* function as an argument (f here is your content-getting function)
(defn data-for-mashup [mashup-name f]  
    {:title mashup-name :content (f)})

;; A sample content getting function
(defn events-content []
  [{:event-name "Great concert"}
   {:event-name "Above Average concert"}])

;; Call the data-for-mashup with the title and the content-getting function
(data-for-mashup "events" events-content)

;; Result
{:title "events"
 :content
    [{:event-name "Great concert"}
     {:event-name "Above Average concert"}]}