2

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

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

宏应该有任何混搭名称和任何功能,这就是我希望它成为宏的原因。

Wnen 我称之为我希望结果看起来像这样

{: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)})  

我究竟做错了什么?

4

2 回答 2

4

首先,您是要创建对 map 的调用还是哈希映射?

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

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

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

user> ({} :a 0)
0

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

接下来, ~@ 从参数中取出顶层嵌套。如果要在列表中返回函数,则需要围绕它构造一个列表。引号(也写为')将防止一对括号导致函数应用,而是构造一个列表。

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"}
于 2013-08-11T16:22:26.827 回答
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"}]}
于 2013-08-12T10:27:36.703 回答