我正在编写一个有点像这样的小数据文件
(top-section 1 "start of text"
(link "bit of text")
(link "bit of text 2"))
我想使用宏来转换上面的表单并在我的系统中处理它,但是我在尝试弄清楚如何使宏的link
部分正常工作时遇到了麻烦。我的链接功能就像这样
(defn link [top-section-id link-text]
....)
正如你所看到的,这需要两个参数,但我上面的定义只传递一个参数。我想要做的是“转换”通过DSL传入的数据,将上面top-section
的id注入到链接函数中。
所以实际上它应该将输入转换为
(top-section 1 "start of text"
(link 1 "bit of text")
(link 1 "bit of text 2"))
如果没有Clojure读者评估代码并抛出错误说我只向link
函数传递了一个参数,我怎么能这样做呢?有没有“逃避”输入,所以它不会评估,直到我做了必要的转换
我知道我可以做到
(top-section 1 "start of text"
'(link "bit of text")
'(link "bit of text 2"))
要获取列表表单,但还有其他方法吗?
答案 0 :(得分:2)
您可以top-section
扩展为特制的let
表单,将link
符号绑定到原始link
函数的部分应用程序到{的第一个参数{1}}形式:
top-section
REPL交互(打印三行,返回(defmacro top-section [n s & forms]
`(let [~'link (partial ~'link ~n)]
(prn ~s) ; handle s in whichever way is appropriate
~@forms))
;; for the sake of example
(defn link [n s] (prn n s))
):
nil
如果user> (top-section 1 "start of text"
(link "more text")
(link "still more"))
"start of text"
1 "more text"
1 "still more"
nil
可能需要嵌套,您可以使用更复杂的top-section
来抓取“命名空间范围”top-section
:
link
在REPL:
(defmacro top-section [n s & forms]
(let [qlink (symbol (name (.. (resolve 'link) ns name)) "link")]
`(let [~'link (partial ~qlink ~n)]
(prn ~s)
~@forms)))
(一个非常可能完全不必要的并发症 - user> (top-section 1 "start of text"
(link "more text")
(link "still more")
(top-section 2 "inner section"
(link "etc.")))
"start of text"
1 "more text"
1 "still more"
"inner section"
2 "etc."
nil
的可配置变体 - 希望它有点令人愉快,如果没有用......)
顺便提一下,你是否有一套小的,固定的功能,你想要以这种方式处理,或者你认为它可能会扩大/变大?在后一种情况下,您可以top-section
对所有符号执行相同的操作,例如在Atom的某个地方:
top-section
在REPL:
(def top-section-syms (atom #{'link}))
(defmacro top-section [n s & forms]
(let [nsym (gensym "n")
qs (for [s @top-section-syms]
[s (symbol (name (.. (resolve s) ns name)) (name s))])]
`(let [~nsym ~n
~@(->> (for [[s q] qs]
[s `(partial ~q ~nsym)])
(apply concat))]
(prn ~s)
~@forms)))
user> (swap! top-section-syms conj 'prn)
#{prn link}
user> (top-section 1 "start of text"
(link "more text")
(link "still more")
(top-section 2 "inner section"
(link "etc.")
(prn "and another fn...")))
"start of text"
1 "more text"
1 "still more"
"inner section"
2 "etc."
2 "and another fn..."
nil
新符号的操作可以用简单的函数/宏(swap!
?)来解决。
答案 1 :(得分:0)
如果top-section
是一个宏,它会使link
表单无法评估,因此它可以以任何方式对其进行转换。
我会建议一些不同的东西:让宏top-section
在一个上下文中评估它的子表单,其中一些动态变量绑定到top-section
的相应参数,并从内部引用它link
函数:
(def ^:dynamic *id*)
(defmacro top-section [id text & body]
`(binding [*id* ,id]
...
~@body))
(defun link [text]
... *id* ...)
答案 2 :(得分:0)
你可以试试这个:
(defmacro transforming [& body]
`(do ~@(map (fn xform [[f arg1 arg2 & more :as syms]]
(if (= f 'top-section)
(apply list f arg1 arg2
(map #(if (= (first %) 'link)
(apply list (first %) arg1 (rest %))
(xform %))
more))
syms))
body)))
然后像这样使用它:
(transforming
(top-section 1 "start of text"
(link "bit of text")
(link "bit of text 2")
(top-section 3 "nested"
(link "nested sections should work too")))
(top-section 2 "section two"
(link "text")
(link "text 2")))
将扩展为:
(do
(top-section 1 "start of text"
(link 1 "bit of text")
(link 1 "bit of text 2")
(top-section 3 "nested"
(link 3 "nested sections should work too")))
(top-section 2 "section two"
(link 2 "text")
(link 2 "text 2")))
然而,这个宏有一个递归调用,我很确定它可以变得更漂亮。