Clojure 将函数转换为宏中的另一种形式
我正在写一个有点像这样的小数据文件Clojure 将函数转换为宏中的另一种形式,clojure,Clojure,我正在写一个有点像这样的小数据文件 (top-section 1 "start of text" (link "bit of text") (link "bit of text 2")) 我想用一个宏来转换上面的表单并在我的系统中处理它,但是我在尝试如何使宏的链接部分正常工作时遇到了困难。我的链接函数是这样的 (defn link [top-section-id link-text] ....) 正如您所看到的,这需要两个参数,但是我上面的定义只传递一个参数。我想做的是“转换
(top-section 1 "start of text"
(link "bit of text")
(link "bit of text 2"))
我想用一个宏来转换上面的表单并在我的系统中处理它,但是我在尝试如何使宏的链接
部分正常工作时遇到了困难。我的链接函数是这样的
(defn link [top-section-id link-text]
....)
正如您所看到的,这需要两个参数,但是我上面的定义只传递一个参数。我想做的是“转换”通过DSL传入的数据,将上面的顶部部分的id注入到link函数中
因此,在现实中,它应该将输入转换为
(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"))
要返回列表表单,还有其他方法吗?如果顶部部分
是一个宏,它将获取未计算的链接
表单,以便可以以任何方式转换它们
不过,我建议做一些不同的事情:让宏top section
在某个动态变量绑定到top section
的相应参数的上下文中评估其子窗体,并从链接
函数中引用:
(def ^:dynamic *id*)
(defmacro top-section [id text & body]
`(binding [*id* ,id]
...
~@body))
(defun link [text]
... *id* ...)
你可以试试这个:
(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")))
但是,这个宏有一个递归调用,我很确定它可以变得更加漂亮。您可以将顶部部分扩展为一个精心编制的让表单将链接
符号绑定到原始链接
函数的部分应用程序,并将其绑定到顶部部分
表格:
(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))
REPL交互(打印三行,nil
返回):
如果顶部部分
s可能需要嵌套,则可以使用更复杂的顶部部分
,该部分会注意获取“名称空间范围”链接
:
(defmacro top-section [n s & forms]
(let [qlink (symbol (name (.. (resolve 'link) ns name)) "link")]
`(let [~'link (partial ~qlink ~n)]
(prn ~s)
~@forms)))
在REPL上:
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
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
(接下来可能是一个完全不必要的复杂问题——顶部部分的一个可配置变量——
——如果没有用处,希望它会有点令人愉快……)
顺便问一下,您是否有一个小的、固定的函数集希望以这种方式处理,或者您认为它可能会扩展/变成大的?在后一种情况下,您可以让顶部部分
对所有符号执行相同的操作,例如在某个原子中:
(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)))
在REPL上:
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
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的操作新符号中的代码可以用简单的函数/宏(register top section symbol
?)来修饰。您认为在这种情况下使用动态变量是最好的选择吗?然而,我是Clojure noob,这似乎有点不必要的偶然性。作为对你第一句话的回应——真的是这样吗?我不断得到算术错误,因为我假设它正在查找链接
函数,然后退出。我需要这些函数在我完成计算之前不被评估transformations@e-i-s IMHO,使用动态变量比在代码上进行魔术转换带来的复杂性更低,因为它不会将功能与精确的数据表示联系起来。考虑当你在另一个函数中包装<代码>链接< /代码>调用时会发生什么。动态变量仍将工作。代码转换将中断。把不需要的东西绑在一起是偶然复杂性的定义,不是吗?@djhworld是的,是的。你的宏对身体形态有什么作用?@Matthias,说得好。但是,当您使用变量解析链接调用中的顶部节id时,这难道不会将链接函数实现与宏联系起来吗?另外,您认为在宏范围中捕获id符号而不是使用名称空间级别的变量怎么样?代码遍历通常是递归的。如果递归是唯一有意义的东西,那么使用递归没有什么错。:)哇,很好的回答,谢谢,这正是我想要的!