将函数转换为宏内的另一种形式

时间:2012-01-29 18:12:29

标签: clojure

我正在编写一个有点像这样的小数据文件

(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"))

要获取列表表单,但还有其他方法吗?

3 个答案:

答案 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")))

然而,这个宏有一个递归调用,我很确定它可以变得更漂亮。