评估 clojure 中的宏参数

发布于 2024-11-28 18:52:50 字数 1600 浏览 3 评论 0原文

我正在尝试将以下宏从 lisp 翻译成 clojure:

(defmacro tag (name atts &body body)
  `(progn (print-tag ',name
                     (list ,@(mapcar (lambda (x)
                                       `(cons ',(car x) ,(cdr x)))
                                     (pairs atts)))
                     nil)
          ,@body
          (print-tag ',name nil t)))

但我一直遇到需要多一级评估的 atts 问题。例如,以下需要评估 t#:

(defmacro tag [tname atts & body]
  `(do (print-tag '~tname '[~@(map (fn [[h# t#]] [h# t#]) (pair atts))] nil)
     ~@body
     (print-tag '~tname nil true)))

因为它会产生类似以下内容:

(tag mytag [color 'blue size 'big])
<mytag color="(quote blue)" size="(quote big)"><\mytag>

我想要评估属性的位置。如果我在上面使用“(eval t#)”,我会遇到这样的问题:

(defn mytag [col] (tag mytag [colour col]))
java.lang.UnsupportedOperationException: Can't eval locals (NO_SOURCE_FILE:1)

有什么建议吗?

为什么 Clojure 中的评估级别似乎少了一层?

支持函数的定义:(

;note doesn't handle nils because I'm dumb
(defn pair [init-lst]
      (loop [lst init-lst item nil pairs []]
    (if (empty? lst)
      pairs
      (if item
        (recur (rest lst) nil (conj pairs [item (first lst)]))
        (recur (rest lst) (first lst) pairs)))))

(defn print-tag [name alst closing]
      (print "<")
      (when closing
    (print "\\"))
      (print name)
      (doall
      (map (fn [[h t]]
           (printf " %s=\"%s\"" h t))
       alst))
      (print ">"))

出于某种原因,我没有像书中那样执行pair函数,这意味着它不能正确处理nils)

I'm trying to translate the following macro from land of lisp into clojure:

(defmacro tag (name atts &body body)
  `(progn (print-tag ',name
                     (list ,@(mapcar (lambda (x)
                                       `(cons ',(car x) ,(cdr x)))
                                     (pairs atts)))
                     nil)
          ,@body
          (print-tag ',name nil t)))

But I keep getting stuck with atts requiring 1 more level of evaluation. E.g. the following needs to evaluate t#:

(defmacro tag [tname atts & body]
  `(do (print-tag '~tname '[~@(map (fn [[h# t#]] [h# t#]) (pair atts))] nil)
     ~@body
     (print-tag '~tname nil true)))

As it produces stuff like:

(tag mytag [color 'blue size 'big])
<mytag color="(quote blue)" size="(quote big)"><\mytag>

Where I want the attribute to evaluated. If I use "(eval t#)" in the above I fall foul of problems like this:

(defn mytag [col] (tag mytag [colour col]))
java.lang.UnsupportedOperationException: Can't eval locals (NO_SOURCE_FILE:1)

Any suggestions?

Why does it seem like one less level of evaluation happens in Clojure?

Definitions of supporting functions:

;note doesn't handle nils because I'm dumb
(defn pair [init-lst]
      (loop [lst init-lst item nil pairs []]
    (if (empty? lst)
      pairs
      (if item
        (recur (rest lst) nil (conj pairs [item (first lst)]))
        (recur (rest lst) (first lst) pairs)))))

(defn print-tag [name alst closing]
      (print "<")
      (when closing
    (print "\\"))
      (print name)
      (doall
      (map (fn [[h t]]
           (printf " %s=\"%s\"" h t))
       alst))
      (print ">"))

(For some reason I didn't do the pair function in the same way as the book which means it doesn't handle nils correctly)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

惟欲睡 2024-12-05 18:52:50

Clojure 的 tag 定义引用属性映射中的所有内容,而 common lisp 版本仅引用名称。这是问题的直接根源 - 如果您只是将 ' 放在矢量/地图前面,然后摆弄 map 以引用第一个元素,那么您'可能会没事。

然而,虽然移植可能是一个很好的练习,但这段代码并不是按照 Clojure 方式编写的:打印是一个令人讨厌的副作用,使得很难使用 print-tag 来做任何有意义的事情;返回一个字符串会好得多。

(defmacro tag [name attrs & body]
  `(str "<" 
        (clojure.string/join " "
                             ['~name
                              ~@(for [[name val] (partition 2 attrs)]
                                  `(str '~name "=\"" ~val "\""))])
        ">"
        ~@body
        "</" '~name ">"))

user> (tag head [foo (+ 1 2)] "TEST" (tag sample []))
"<head foo=\"3\">TEST<sample></sample></head>"

当然,由于顺序并不重要,因此使用映射而不是向量对于属性来说更好。这也意味着您可以删除 (partition 2...),因为地图的顺序视图已经成对出现。

一旦我们走到这一步,就会发现已经有很多方法可以将 XML 表示为 Clojure 数据结构,所以我永远不会在实际应用程序中使用上面的代码。如果您想真正使用 XML,请查看 Hiccupprxml,或 data.xml

Your Clojure definition of tag quotes everything in the attribute map, while the common lisp version quotes only the names. That's the immediate source of your problems - if you just dropped the ' in front of your vector/map, and then fiddled with the map to quote the first element, you'd probably be fine.

However, while porting may be a good exercise, this code is not written in The Clojure Way: the printing is a nasty ucky side effect that makes it hard to use print-tag to do anything meaningful; returning a string instead would be much nicer.

(defmacro tag [name attrs & body]
  `(str "<" 
        (clojure.string/join " "
                             ['~name
                              ~@(for [[name val] (partition 2 attrs)]
                                  `(str '~name "=\"" ~val "\""))])
        ">"
        ~@body
        "</" '~name ">"))

user> (tag head [foo (+ 1 2)] "TEST" (tag sample []))
"<head foo=\"3\">TEST<sample></sample></head>"

Of course, since order doesn't matter, using a map instead of a vector is nicer for the attributes. That would also mean you could drop the (partition 2...), since a sequential view of a map comes out as pairs already.

And once we've gotten this far, it turns out that there are already plenty of ways to represent XML as Clojure data structures, so I would never use my above code in a real application. If you want to do XML for real, check out any of Hiccup, prxml, or data.xml.

黑寡妇 2024-12-05 18:52:50

我可能会遗漏一些东西,但是有一个特殊的原因你引用了蓝色和大而不是颜色和大小,你还在宏中引用了向量,所以里面的东西不会被评估,如果你在向量周围加上引号并且也引用颜色和大你会得到你想要的,


(defmacro tag [tname atts & body]
  `(do (print-tag '~tname [~@(map (fn [[h# t#]] [h# t#]) (pair atts))] nil)
       ~@body
       (print-tag '~tname nil true)))

(tag mytag ['color 'blue 'size 'big])

<mytag color="blue" size="big"><\mytag>nil

只是为了记录而不是使用关键字的符号将是更惯用的 clojure 。

I might be missing something but is there a particular reason you quoted blue and big but not color and size, you also quoted in the macro the vector so stuff inside it won't get evaluated, if you drop the quote around the vector and also quote color and big you get what you want,


(defmacro tag [tname atts & body]
  `(do (print-tag '~tname [~@(map (fn [[h# t#]] [h# t#]) (pair atts))] nil)
       ~@body
       (print-tag '~tname nil true)))

(tag mytag ['color 'blue 'size 'big])

<mytag color="blue" size="big"><\mytag>nil

Just for the record instead of symbols using keywords would be more idiomatic clojure for this.

九公里浅绿 2024-12-05 18:52:50

为了完整起见,我想要的结果是:

(defmacro tag [tname atts & body]
  `(do (print-tag '~tname [~@(map (fn [[h# t#]] [`'~h# t#]) (pair atts))] nil)
     ~@body
     (print-tag '~tname nil true)))

For the sake of completeness, what I wanted turned out to be:

(defmacro tag [tname atts & body]
  `(do (print-tag '~tname [~@(map (fn [[h# t#]] [`'~h# t#]) (pair atts))] nil)
     ~@body
     (print-tag '~tname nil true)))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文